[llvm] [AArch64][GlobalISel] Add legalisation for G_INSERT_SUBVECTOR (PR #205349)

Joshua Rodriguez via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 25 07:25:45 PDT 2026


https://github.com/JoshdRod updated https://github.com/llvm/llvm-project/pull/205349

>From 3f8492ab8df093256f6000789058fcd96c081862 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Fri, 12 Jun 2026 10:17:39 +0000
Subject: [PATCH 01/10] [AArch64][GlobalISel] Add basic legalisation rules for
 G_INSERT_SUBVECTOR

---
 .../AArch64/GISel/AArch64LegalizerInfo.cpp     | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index e6521a211226e..89dfeb97c82cc 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -400,6 +400,24 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
       .scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
       .scalarSameSizeAs(0, 1);
 
+  getActionDefinitionsBuilder(G_INSERT_SUBVECTOR)
+      .legalFor({{v16i8, v16i8},
+		 {v16i8, v8i8},
+                 {v8i16, v8i16},
+		 {v8i16, v4i16},
+                 {v4i32, v4i32},
+		 {v4i32, v2i32}})
+      .immIdx(0); // Inform verifier imm idx 0 is handled.
+      //.widenScalarToNextPow2(1, /*Min=*/32)
+      //.clampScalar(1, s32, s64)
+      //.widenScalarOrEltToNextPow2OrMinSize(1, /*Min=*/8)
+      //.clampNumElements(0, v8s8, v16s8)
+      //.clampNumElements(0, v4s16, v8s16)
+      //.clampNumElements(0, v2s32, v4s32)
+      //.moreElementsToNextPow2(0)
+      //.scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
+      //.scalarSameSizeAs(0, 1);
+
   getActionDefinitionsBuilder(G_CTLZ_ZERO_POISON).lower();
 
   getActionDefinitionsBuilder(G_CTTZ)

>From 0d271ab65bf3601e8c0216a8adc976953971c0cc Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Fri, 12 Jun 2026 15:17:37 +0000
Subject: [PATCH 02/10] [AArch64][GlobalISel]! Add support for half-and-half
 subvector inserts

Some subvector inserts involve inserting half of one vector into another.
This can be lowered to concat(subvector, extract(vector)).
---
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    | 40 ++++++++++++++++++-
 llvm/lib/Target/AArch64/AArch64InstrGISel.td  |  1 +
 .../AArch64/GISel/AArch64LegalizerInfo.cpp    | 15 +++----
 3 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 8c38d8ce79d59..8ca8b57cf1082 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4572,7 +4572,6 @@ void LegalizerHelper::changeOpcode(MachineInstr &MI, unsigned NewOpcode) {
 LegalizerHelper::LegalizeResult
 LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
   using namespace TargetOpcode;
-
   switch(MI.getOpcode()) {
   default:
     return UnableToLegalize;
@@ -4877,6 +4876,45 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
     return lowerVECTOR_COMPRESS(MI);
   case G_DYN_STACKALLOC:
     return lowerDynStackAlloc(MI);
+  case G_INSERT_SUBVECTOR: {
+    if (MRI.getType(MI.getOperand(1).getReg()).isScalable()
+        || MRI.getType(MI.getOperand(2).getReg()).isScalable())
+      return UnableToLegalize;
+
+    // Check that subvector is half size of main vector
+    Register Vector = MI.getOperand(1).getReg();
+    Register Subvector = MI.getOperand(2).getReg();
+    auto insertionPointImm = MI.getOperand(3).getImm();
+
+    LLT VectorTy = MRI.getType(Vector);
+    LLT SubvectorTy = MRI.getType(Subvector);
+    // If so, -> concat(subvector, extract(half of vector))
+    // (Operands can be either way round depending on insertion point
+    if (VectorTy.getSizeInBits() == SubvectorTy.getSizeInBits() * 2)
+    {
+      bool insertInLowHalf = insertionPointImm == 0;
+      auto extract = MIRBuilder.buildInstr(TargetOpcode::G_EXTRACT_SUBVECTOR,
+		      {SubvectorTy},
+		      {Vector, (uint64_t)(insertInLowHalf ? VectorTy.getElementCount().getKnownMinValue() / 2 : 0)});
+      
+      auto LowHalf = insertInLowHalf ? Subvector : extract.getReg(0);
+      auto HighHalf = insertInLowHalf ? extract.getReg(0) : Subvector;
+
+      MIRBuilder.buildInstr(TargetOpcode::G_CONCAT_VECTORS,
+			{MI.getOperand(0)},
+			{LowHalf, HighHalf});
+      MI.eraseFromParent();
+      return Legalized;
+    }
+    // Else -> shuffle
+    else
+    {
+	    return UnableToLegalize;
+    }
+    // Look up what sdag does**
+    // The g opcodes should all be supported by gi
+    // Make sdag tests works for GI
+  }
   case G_STACKSAVE:
     return lowerStackSave(MI);
   case G_STACKRESTORE:
diff --git a/llvm/lib/Target/AArch64/AArch64InstrGISel.td b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
index ec95448f61043..f56deb14b8a5f 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrGISel.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
@@ -349,6 +349,7 @@ def : GINodeEquiv<G_SLI, AArch64vsli>;
 def : GINodeEquiv<G_SRI, AArch64vsri>;
 
 def : GINodeEquiv<G_EXTRACT_VECTOR_ELT, vector_extract>;
+def : GINodeEquiv<G_INSERT_SUBVECTOR, insert_subvector>;
 
 def : GINodeEquiv<G_AARCH64_PREFETCH, AArch64Prefetch>;
 def : GINodeEquiv<G_AARCH64_RANGE_PREFETCH, AArch64RangePrefetch>;
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 89dfeb97c82cc..4273fae5fa118 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -401,13 +401,14 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
       .scalarSameSizeAs(0, 1);
 
   getActionDefinitionsBuilder(G_INSERT_SUBVECTOR)
-      .legalFor({{v16i8, v16i8},
-		 {v16i8, v8i8},
-                 {v8i16, v8i16},
-		 {v8i16, v4i16},
-                 {v4i32, v4i32},
-		 {v4i32, v2i32}})
-      .immIdx(0); // Inform verifier imm idx 0 is handled.
+      .lower(); // go to legalizerhelper.cpp
+      //.legalFor({{v16i8, v16i8},
+      //  	 {v16i8, v8i8},
+      //           {v8i16, v8i16},
+      //  	 {v8i16, v4i16},
+      //           {v4i32, v4i32},
+      //  	 {v4i32, v2i32}})
+      //.immIdx(0); // Inform verifier imm idx 0 is handled.
       //.widenScalarToNextPow2(1, /*Min=*/32)
       //.clampScalar(1, s32, s64)
       //.widenScalarOrEltToNextPow2OrMinSize(1, /*Min=*/8)

>From 5fba05378f2ac1f98042dd07085d89b721f0e4d4 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Fri, 19 Jun 2026 16:03:28 +0000
Subject: [PATCH 03/10] [AArch64][GlobalISel] Add support for irregular-sized
 subvector inserts

There are two types of subvector inserts.
1. Half-and-half
Vector V is double the size of subvector W. A subvector insert of W into V can be expressed as concat(W, extract(correct half of V)).
2. Irregular
V is NOT double the size of W. Hence, we must express a subvector insert of W into V as a shuffle(V, W, Mask).

Add logic for this shuffle(V, W, Mask).

Note: Shuffle takes 2 vectors of equal length and returns a vector with some of the elements of its inputs, in an order determined by the mask.
The way it gets to its result can be thought of like this: the V and W are concatenated into one big vector, and the mask's elements map to the indices in the big vector of each element in the resulting vector.
---
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    | 22 +++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 8ca8b57cf1082..ddcaeaa558095 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4906,10 +4906,28 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
       MI.eraseFromParent();
       return Legalized;
     }
-    // Else -> shuffle
+    // Else -> shuffle(vector, extend(subvector, size(vector)), mask)
     else
     {
-	    return UnableToLegalize;
+	    // Extend subvector to same size as vector
+	    Register ExtendedSubvector = MRI.createGenericVirtualRegister(VectorTy);
+	    MIRBuilder.buildPadVectorWithUndefElements(ExtendedSubvector, Subvector);
+
+	    // Calculate mask required for this shuffle
+	    SmallVector<int> Mask;
+	    for (int i; i < VectorTy.getElementCount().getKnownMinValue(); i++)
+	    {
+		// If this index is within bounds, put subvector's index into mask
+		if (i > insertionPointImm && i < insertionPointImm + SubvectorTy.getElementCount().getKnownMinValue())
+			Mask.push_back(VectorTy.getElementCount().getKnownMinValue() + i - insertionPointImm);
+		else
+			Mask.push_back(i);
+	    }
+
+	    // Build shuffle
+	    MIRBuilder.buildShuffleVector(MI.getOperand(0), Vector, ExtendedSubvector, Mask);
+	    MI.eraseFromParent();
+	    return Legalized;
     }
     // Look up what sdag does**
     // The g opcodes should all be supported by gi

>From d91f629c0e27feb0e8ad5b6fa34ea03fe4d8c38d Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Tue, 23 Jun 2026 13:46:09 +0000
Subject: [PATCH 04/10] [AArch64][GlobalISel] Update test checks

---
 .../GlobalISel/legalizer-info-validation.mir  |  5 +-
 .../CodeGen/AArch64/extract-vector-elt.ll     | 66 ++++++++++++-------
 2 files changed, 47 insertions(+), 24 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index 190761c4124e0..8aa1c2679ac3e 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -1,3 +1,4 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple=aarch64-- -run-pass=legalizer %s \
 # RUN:     -mcpu=cortex-a75 -o - 2>&1 | FileCheck %s --check-prefixes=CHECK
 
@@ -724,8 +725,8 @@
 # DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
 # DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
 # DEBUG-NEXT: G_INSERT_SUBVECTOR (opcode {{[0-9]+}}): 2 type indices, 1 imm index
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: G_EXTRACT_SUBVECTOR (opcode {{[0-9]+}}): 2 type indices, 1 imm index
 # DEBUG-NEXT: .. the first uncovered type index: 2, OK
 # DEBUG-NEXT: .. the first uncovered imm index: 1, OK
diff --git a/llvm/test/CodeGen/AArch64/extract-vector-elt.ll b/llvm/test/CodeGen/AArch64/extract-vector-elt.ll
index f05b37435f83c..d0ca98ee94299 100644
--- a/llvm/test/CodeGen/AArch64/extract-vector-elt.ll
+++ b/llvm/test/CodeGen/AArch64/extract-vector-elt.ll
@@ -2,9 +2,7 @@
 ; RUN: llc -mtriple=aarch64 -verify-machineinstrs %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SD
 ; RUN: llc -mtriple=aarch64 -global-isel -global-isel-abort=2 -verify-machineinstrs %s -o - 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-GI
 
-; CHECK-GI:       warning: Instruction selection used fallback path for extract_v4i32_vector_insert
-; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for extract_v4i32_vector_insert_const
-; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for extract_v4i32_vector_extract
+; CHECK-GI:       warning: Instruction selection used fallback path for extract_v4i32_vector_extract
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for extract_v4i32_vector_extract_const
 
 define i64 @extract_v2i64_undef_index(<2 x i64> %a, i32 %c) {
@@ -761,20 +759,35 @@ entry:
 }
 
 define i32 @extract_v4i32_vector_insert(<4 x i32> %a, <2 x i32> %b, i32 %c) {
-; CHECK-LABEL: extract_v4i32_vector_insert:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    sub sp, sp, #16
-; CHECK-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-NEXT:    mov d0, v0.d[1]
-; CHECK-NEXT:    // kill: def $d1 killed $d1 def $q1
-; CHECK-NEXT:    mov x8, sp
-; CHECK-NEXT:    // kill: def $w0 killed $w0 def $x0
-; CHECK-NEXT:    bfi x8, x0, #2, #2
-; CHECK-NEXT:    mov v1.d[1], v0.d[0]
-; CHECK-NEXT:    str q1, [sp]
-; CHECK-NEXT:    ldr w0, [x8]
-; CHECK-NEXT:    add sp, sp, #16
-; CHECK-NEXT:    ret
+; CHECK-SD-LABEL: extract_v4i32_vector_insert:
+; CHECK-SD:       // %bb.0: // %entry
+; CHECK-SD-NEXT:    sub sp, sp, #16
+; CHECK-SD-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-SD-NEXT:    mov d0, v0.d[1]
+; CHECK-SD-NEXT:    // kill: def $d1 killed $d1 def $q1
+; CHECK-SD-NEXT:    mov x8, sp
+; CHECK-SD-NEXT:    // kill: def $w0 killed $w0 def $x0
+; CHECK-SD-NEXT:    bfi x8, x0, #2, #2
+; CHECK-SD-NEXT:    mov v1.d[1], v0.d[0]
+; CHECK-SD-NEXT:    str q1, [sp]
+; CHECK-SD-NEXT:    ldr w0, [x8]
+; CHECK-SD-NEXT:    add sp, sp, #16
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: extract_v4i32_vector_insert:
+; CHECK-GI:       // %bb.0: // %entry
+; CHECK-GI-NEXT:    sub sp, sp, #16
+; CHECK-GI-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-GI-NEXT:    mov d0, v0.d[1]
+; CHECK-GI-NEXT:    // kill: def $d1 killed $d1 def $q1
+; CHECK-GI-NEXT:    mov w8, w0
+; CHECK-GI-NEXT:    mov x9, sp
+; CHECK-GI-NEXT:    and x8, x8, #0x3
+; CHECK-GI-NEXT:    mov v1.d[1], v0.d[0]
+; CHECK-GI-NEXT:    str q1, [sp]
+; CHECK-GI-NEXT:    ldr w0, [x9, x8, lsl #2]
+; CHECK-GI-NEXT:    add sp, sp, #16
+; CHECK-GI-NEXT:    ret
 entry:
   %vector = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> %a, <2 x i32> %b, i64 0)
   %d = extractelement <4 x i32> %vector, i32 %c
@@ -782,11 +795,20 @@ entry:
 }
 
 define i32 @extract_v4i32_vector_insert_const(<4 x i32> %a, <2 x i32> %b, i32 %c) {
-; CHECK-LABEL: extract_v4i32_vector_insert_const:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    // kill: def $d1 killed $d1 def $q1
-; CHECK-NEXT:    mov w0, v1.s[1]
-; CHECK-NEXT:    ret
+; CHECK-SD-LABEL: extract_v4i32_vector_insert_const:
+; CHECK-SD:       // %bb.0: // %entry
+; CHECK-SD-NEXT:    // kill: def $d1 killed $d1 def $q1
+; CHECK-SD-NEXT:    mov w0, v1.s[1]
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: extract_v4i32_vector_insert_const:
+; CHECK-GI:       // %bb.0: // %entry
+; CHECK-GI-NEXT:    mov d0, v0.d[1]
+; CHECK-GI-NEXT:    // kill: def $d1 killed $d1 def $q1
+; CHECK-GI-NEXT:    mov v1.d[1], v0.d[0]
+; CHECK-GI-NEXT:    mov s0, v1.s[1]
+; CHECK-GI-NEXT:    fmov w0, s0
+; CHECK-GI-NEXT:    ret
 entry:
   %vector = call <4 x i32> @llvm.vector.insert.v4i32.v2i32(<4 x i32> %a, <2 x i32> %b, i64 0)
   %d = extractelement <4 x i32> %vector, i32 1

>From 0f160cb16d406053241d2305a995f893ef244416 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Wed, 24 Jun 2026 13:22:13 +0000
Subject: [PATCH 05/10] [AArch64][GlobalISel] Run clang-format

---
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    | 67 ++++++++++---------
 .../AArch64/GISel/AArch64LegalizerInfo.cpp    | 32 ++++-----
 2 files changed, 51 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index ddcaeaa558095..be84a518d321e 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4877,8 +4877,8 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
   case G_DYN_STACKALLOC:
     return lowerDynStackAlloc(MI);
   case G_INSERT_SUBVECTOR: {
-    if (MRI.getType(MI.getOperand(1).getReg()).isScalable()
-        || MRI.getType(MI.getOperand(2).getReg()).isScalable())
+    if (MRI.getType(MI.getOperand(1).getReg()).isScalable() ||
+        MRI.getType(MI.getOperand(2).getReg()).isScalable())
       return UnableToLegalize;
 
     // Check that subvector is half size of main vector
@@ -4890,44 +4890,47 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
     LLT SubvectorTy = MRI.getType(Subvector);
     // If so, -> concat(subvector, extract(half of vector))
     // (Operands can be either way round depending on insertion point
-    if (VectorTy.getSizeInBits() == SubvectorTy.getSizeInBits() * 2)
-    {
+    if (VectorTy.getSizeInBits() == SubvectorTy.getSizeInBits() * 2) {
       bool insertInLowHalf = insertionPointImm == 0;
-      auto extract = MIRBuilder.buildInstr(TargetOpcode::G_EXTRACT_SUBVECTOR,
-		      {SubvectorTy},
-		      {Vector, (uint64_t)(insertInLowHalf ? VectorTy.getElementCount().getKnownMinValue() / 2 : 0)});
-      
+      auto extract = MIRBuilder.buildInstr(
+          TargetOpcode::G_EXTRACT_SUBVECTOR, {SubvectorTy},
+          {Vector,
+           (uint64_t)(insertInLowHalf
+                          ? VectorTy.getElementCount().getKnownMinValue() / 2
+                          : 0)});
+
       auto LowHalf = insertInLowHalf ? Subvector : extract.getReg(0);
       auto HighHalf = insertInLowHalf ? extract.getReg(0) : Subvector;
 
-      MIRBuilder.buildInstr(TargetOpcode::G_CONCAT_VECTORS,
-			{MI.getOperand(0)},
-			{LowHalf, HighHalf});
+      MIRBuilder.buildInstr(TargetOpcode::G_CONCAT_VECTORS, {MI.getOperand(0)},
+                            {LowHalf, HighHalf});
       MI.eraseFromParent();
       return Legalized;
     }
     // Else -> shuffle(vector, extend(subvector, size(vector)), mask)
-    else
-    {
-	    // Extend subvector to same size as vector
-	    Register ExtendedSubvector = MRI.createGenericVirtualRegister(VectorTy);
-	    MIRBuilder.buildPadVectorWithUndefElements(ExtendedSubvector, Subvector);
-
-	    // Calculate mask required for this shuffle
-	    SmallVector<int> Mask;
-	    for (int i; i < VectorTy.getElementCount().getKnownMinValue(); i++)
-	    {
-		// If this index is within bounds, put subvector's index into mask
-		if (i > insertionPointImm && i < insertionPointImm + SubvectorTy.getElementCount().getKnownMinValue())
-			Mask.push_back(VectorTy.getElementCount().getKnownMinValue() + i - insertionPointImm);
-		else
-			Mask.push_back(i);
-	    }
-
-	    // Build shuffle
-	    MIRBuilder.buildShuffleVector(MI.getOperand(0), Vector, ExtendedSubvector, Mask);
-	    MI.eraseFromParent();
-	    return Legalized;
+    else {
+      // Extend subvector to same size as vector
+      Register ExtendedSubvector = MRI.createGenericVirtualRegister(VectorTy);
+      MIRBuilder.buildPadVectorWithUndefElements(ExtendedSubvector, Subvector);
+
+      // Calculate mask required for this shuffle
+      SmallVector<int> Mask;
+      for (int i; i < VectorTy.getElementCount().getKnownMinValue(); i++) {
+        // If this index is within bounds, put subvector's index into mask
+        if (i > insertionPointImm &&
+            i < insertionPointImm +
+                    SubvectorTy.getElementCount().getKnownMinValue())
+          Mask.push_back(VectorTy.getElementCount().getKnownMinValue() + i -
+                         insertionPointImm);
+        else
+          Mask.push_back(i);
+      }
+
+      // Build shuffle
+      MIRBuilder.buildShuffleVector(MI.getOperand(0), Vector, ExtendedSubvector,
+                                    Mask);
+      MI.eraseFromParent();
+      return Legalized;
     }
     // Look up what sdag does**
     // The g opcodes should all be supported by gi
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 4273fae5fa118..9074c25950432 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -402,22 +402,22 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
 
   getActionDefinitionsBuilder(G_INSERT_SUBVECTOR)
       .lower(); // go to legalizerhelper.cpp
-      //.legalFor({{v16i8, v16i8},
-      //  	 {v16i8, v8i8},
-      //           {v8i16, v8i16},
-      //  	 {v8i16, v4i16},
-      //           {v4i32, v4i32},
-      //  	 {v4i32, v2i32}})
-      //.immIdx(0); // Inform verifier imm idx 0 is handled.
-      //.widenScalarToNextPow2(1, /*Min=*/32)
-      //.clampScalar(1, s32, s64)
-      //.widenScalarOrEltToNextPow2OrMinSize(1, /*Min=*/8)
-      //.clampNumElements(0, v8s8, v16s8)
-      //.clampNumElements(0, v4s16, v8s16)
-      //.clampNumElements(0, v2s32, v4s32)
-      //.moreElementsToNextPow2(0)
-      //.scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
-      //.scalarSameSizeAs(0, 1);
+                //.legalFor({{v16i8, v16i8},
+                //  	 {v16i8, v8i8},
+                //           {v8i16, v8i16},
+                //  	 {v8i16, v4i16},
+                //           {v4i32, v4i32},
+                //  	 {v4i32, v2i32}})
+                //.immIdx(0); // Inform verifier imm idx 0 is handled.
+                //.widenScalarToNextPow2(1, /*Min=*/32)
+                //.clampScalar(1, s32, s64)
+                //.widenScalarOrEltToNextPow2OrMinSize(1, /*Min=*/8)
+                //.clampNumElements(0, v8s8, v16s8)
+                //.clampNumElements(0, v4s16, v8s16)
+                //.clampNumElements(0, v2s32, v4s32)
+                //.moreElementsToNextPow2(0)
+                //.scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
+                //.scalarSameSizeAs(0, 1);
 
   getActionDefinitionsBuilder(G_CTLZ_ZERO_POISON).lower();
 

>From e62de04ee54c4127336174a3432448c303b798d3 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Wed, 24 Jun 2026 13:40:50 +0000
Subject: [PATCH 06/10] [AArch64][GlobalISel] Remove comments

---
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    |  3 ---
 .../AArch64/GISel/AArch64LegalizerInfo.cpp    | 19 +------------------
 .../GlobalISel/legalizer-info-validation.mir  |  1 -
 3 files changed, 1 insertion(+), 22 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index be84a518d321e..09c5e35b6d4e7 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4932,9 +4932,6 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
       MI.eraseFromParent();
       return Legalized;
     }
-    // Look up what sdag does**
-    // The g opcodes should all be supported by gi
-    // Make sdag tests works for GI
   }
   case G_STACKSAVE:
     return lowerStackSave(MI);
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 9074c25950432..156af2dd310e4 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -400,24 +400,7 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
       .scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
       .scalarSameSizeAs(0, 1);
 
-  getActionDefinitionsBuilder(G_INSERT_SUBVECTOR)
-      .lower(); // go to legalizerhelper.cpp
-                //.legalFor({{v16i8, v16i8},
-                //  	 {v16i8, v8i8},
-                //           {v8i16, v8i16},
-                //  	 {v8i16, v4i16},
-                //           {v4i32, v4i32},
-                //  	 {v4i32, v2i32}})
-                //.immIdx(0); // Inform verifier imm idx 0 is handled.
-                //.widenScalarToNextPow2(1, /*Min=*/32)
-                //.clampScalar(1, s32, s64)
-                //.widenScalarOrEltToNextPow2OrMinSize(1, /*Min=*/8)
-                //.clampNumElements(0, v8s8, v16s8)
-                //.clampNumElements(0, v4s16, v8s16)
-                //.clampNumElements(0, v2s32, v4s32)
-                //.moreElementsToNextPow2(0)
-                //.scalarizeIf(scalarOrEltWiderThan(0, 32), 0)
-                //.scalarSameSizeAs(0, 1);
+  getActionDefinitionsBuilder(G_INSERT_SUBVECTOR).lower();
 
   getActionDefinitionsBuilder(G_CTLZ_ZERO_POISON).lower();
 
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index 8aa1c2679ac3e..26c0aaa3bb3c9 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -1,4 +1,3 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple=aarch64-- -run-pass=legalizer %s \
 # RUN:     -mcpu=cortex-a75 -o - 2>&1 | FileCheck %s --check-prefixes=CHECK
 

>From 4c708511e1bdd4a620ea184d8e5747faab37a9a2 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Thu, 25 Jun 2026 13:05:29 +0000
Subject: [PATCH 07/10] [AArch64][GlobalISel] Make variable names uppercase

---
 .../lib/CodeGen/GlobalISel/LegalizerHelper.cpp | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 09c5e35b6d4e7..4775bd71d8d6f 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4884,23 +4884,23 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
     // Check that subvector is half size of main vector
     Register Vector = MI.getOperand(1).getReg();
     Register Subvector = MI.getOperand(2).getReg();
-    auto insertionPointImm = MI.getOperand(3).getImm();
+    auto InsertionPointImm = MI.getOperand(3).getImm();
 
     LLT VectorTy = MRI.getType(Vector);
     LLT SubvectorTy = MRI.getType(Subvector);
     // If so, -> concat(subvector, extract(half of vector))
     // (Operands can be either way round depending on insertion point
     if (VectorTy.getSizeInBits() == SubvectorTy.getSizeInBits() * 2) {
-      bool insertInLowHalf = insertionPointImm == 0;
-      auto extract = MIRBuilder.buildInstr(
+      bool InsertInLowHalf = InsertionPointImm == 0;
+      auto Extract = MIRBuilder.buildInstr(
           TargetOpcode::G_EXTRACT_SUBVECTOR, {SubvectorTy},
           {Vector,
-           (uint64_t)(insertInLowHalf
+           (uint64_t)(InsertInLowHalf
                           ? VectorTy.getElementCount().getKnownMinValue() / 2
                           : 0)});
 
-      auto LowHalf = insertInLowHalf ? Subvector : extract.getReg(0);
-      auto HighHalf = insertInLowHalf ? extract.getReg(0) : Subvector;
+      auto LowHalf = InsertInLowHalf ? Subvector : Extract.getReg(0);
+      auto HighHalf = InsertInLowHalf ? Extract.getReg(0) : Subvector;
 
       MIRBuilder.buildInstr(TargetOpcode::G_CONCAT_VECTORS, {MI.getOperand(0)},
                             {LowHalf, HighHalf});
@@ -4917,11 +4917,11 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
       SmallVector<int> Mask;
       for (int i; i < VectorTy.getElementCount().getKnownMinValue(); i++) {
         // If this index is within bounds, put subvector's index into mask
-        if (i > insertionPointImm &&
-            i < insertionPointImm +
+        if (i > InsertionPointImm &&
+            i < InsertionPointImm +
                     SubvectorTy.getElementCount().getKnownMinValue())
           Mask.push_back(VectorTy.getElementCount().getKnownMinValue() + i -
-                         insertionPointImm);
+                         InsertionPointImm);
         else
           Mask.push_back(i);
       }

>From 9b405a7f7319fa43f2c53895b4a5f2e8efb43375 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Thu, 25 Jun 2026 13:27:03 +0000
Subject: [PATCH 08/10] [AArch64][GlobalISel] Replace buildInstr with
 buildExtractSubvector

As funtion name is more explicit and signature is more concise, improving readability.
---
 llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 4775bd71d8d6f..6bb0d758d82a0 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4892,12 +4892,11 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
     // (Operands can be either way round depending on insertion point
     if (VectorTy.getSizeInBits() == SubvectorTy.getSizeInBits() * 2) {
       bool InsertInLowHalf = InsertionPointImm == 0;
-      auto Extract = MIRBuilder.buildInstr(
-          TargetOpcode::G_EXTRACT_SUBVECTOR, {SubvectorTy},
-          {Vector,
-           (uint64_t)(InsertInLowHalf
-                          ? VectorTy.getElementCount().getKnownMinValue() / 2
-                          : 0)});
+      auto Extract = MIRBuilder.buildExtractSubvector(
+          SubvectorTy, Vector,
+          (uint64_t)(InsertInLowHalf
+                         ? VectorTy.getElementCount().getKnownMinValue() / 2
+                         : 0));
 
       auto LowHalf = InsertInLowHalf ? Subvector : Extract.getReg(0);
       auto HighHalf = InsertInLowHalf ? Extract.getReg(0) : Subvector;

>From af72cc681ec272410228b0cde762a8c6699c124c Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Thu, 25 Jun 2026 14:07:15 +0000
Subject: [PATCH 09/10] [AArch64][GlobalISel] Refactor
 getElementCount().getKnownMinValue() to getNumElements()

As the start of the block checks if the operands are scalable, we can be sure that at this point the operands are non-scalable. In that case, rewrite the function call to the more concise getNumElements().
---
 llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 6bb0d758d82a0..5afd078775848 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -4894,9 +4894,7 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
       bool InsertInLowHalf = InsertionPointImm == 0;
       auto Extract = MIRBuilder.buildExtractSubvector(
           SubvectorTy, Vector,
-          (uint64_t)(InsertInLowHalf
-                         ? VectorTy.getElementCount().getKnownMinValue() / 2
-                         : 0));
+          (uint64_t)(InsertInLowHalf ? VectorTy.getNumElements() / 2 : 0));
 
       auto LowHalf = InsertInLowHalf ? Subvector : Extract.getReg(0);
       auto HighHalf = InsertInLowHalf ? Extract.getReg(0) : Subvector;
@@ -4914,13 +4912,11 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
 
       // Calculate mask required for this shuffle
       SmallVector<int> Mask;
-      for (int i; i < VectorTy.getElementCount().getKnownMinValue(); i++) {
+      for (int i; i < VectorTy.getNumElements(); i++) {
         // If this index is within bounds, put subvector's index into mask
         if (i > InsertionPointImm &&
-            i < InsertionPointImm +
-                    SubvectorTy.getElementCount().getKnownMinValue())
-          Mask.push_back(VectorTy.getElementCount().getKnownMinValue() + i -
-                         InsertionPointImm);
+            i < InsertionPointImm + SubvectorTy.getNumElements())
+          Mask.push_back(VectorTy.getNumElements() + i - InsertionPointImm);
         else
           Mask.push_back(i);
       }

>From e5b43de8d96feae3aed2744b28aa45286eb73c41 Mon Sep 17 00:00:00 2001
From: Josh Rodriguez <josh.rodriguez at arm.com>
Date: Thu, 25 Jun 2026 14:25:21 +0000
Subject: [PATCH 10/10] [AArch64][GlobalISel] Remove erroneous GINodeEquiv
 statement

Statement was added whilst I was testing the possibility of handling G_INSERT_SUBVECTOR in Instruction Selection, and having no legalisation rules for it. The approach didn't work, and I forgot to delete the remains.
Remove this statement.
---
 llvm/lib/Target/AArch64/AArch64InstrGISel.td | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Target/AArch64/AArch64InstrGISel.td b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
index f56deb14b8a5f..ec95448f61043 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrGISel.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrGISel.td
@@ -349,7 +349,6 @@ def : GINodeEquiv<G_SLI, AArch64vsli>;
 def : GINodeEquiv<G_SRI, AArch64vsri>;
 
 def : GINodeEquiv<G_EXTRACT_VECTOR_ELT, vector_extract>;
-def : GINodeEquiv<G_INSERT_SUBVECTOR, insert_subvector>;
 
 def : GINodeEquiv<G_AARCH64_PREFETCH, AArch64Prefetch>;
 def : GINodeEquiv<G_AARCH64_RANGE_PREFETCH, AArch64RangePrefetch>;



More information about the llvm-commits mailing list