[llvm] [AArch64] Reorganise perfect shuffle generation. NFC (PR #224526)

David Green via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 26 04:30:20 PDT 2026


https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/224526

>From a9245c5d064e0f687dfaf159e794cb7beb28e670 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Sat, 26 Sep 2026 12:29:59 +0100
Subject: [PATCH] [AArch64] Reorganise perfect shuffle generation. NFC

This adds a generetePerfectShuffle implementation for parsing through the
perfect shuffle tables, generating a list of ShuffleEntry's that represent the
sequence of shuffles that need to be performed. This is intended to be a NFC
as-is, allowing it to be reused to global isel and extended in the future to
handle shuffles that are not part of the shuffle table. The number of
instructions generated can also be used for costing shuffles, as we do for
immediate generation.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 321 ++++++++----------
 .../Target/AArch64/AArch64PerfectShuffle.h    | 119 +++++++
 2 files changed, 259 insertions(+), 181 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index d61304154cad7..c98d607c4a22c 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -15669,174 +15669,6 @@ static SDValue tryFormConcatFromShuffle(SDValue Op, SelectionDAG &DAG) {
   return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, V0, V1);
 }
 
-/// GeneratePerfectShuffle - Given an entry in the perfect-shuffle table, emit
-/// the specified operations to build the shuffle. ID is the perfect-shuffle
-//ID, V1 and V2 are the original shuffle inputs. PFEntry is the Perfect shuffle
-//table entry and LHS/RHS are the immediate inputs for this stage of the
-//shuffle.
-static SDValue GeneratePerfectShuffle(unsigned ID, SDValue V1, SDValue V2,
-                                      unsigned PFEntry, SDValue LHS,
-                                      SDValue RHS, SelectionDAG &DAG,
-                                      const SDLoc &DL) {
-  unsigned OpNum = (PFEntry >> 26) & 0x0F;
-  unsigned LHSID = (PFEntry >> 13) & ((1 << 13) - 1);
-  unsigned RHSID = (PFEntry >> 0) & ((1 << 13) - 1);
-
-  enum {
-    OP_COPY = 0, // Copy, used for things like <u,u,u,3> to say it is <0,1,2,3>
-    OP_VREV,
-    OP_VDUP0,
-    OP_VDUP1,
-    OP_VDUP2,
-    OP_VDUP3,
-    OP_VEXT1,
-    OP_VEXT2,
-    OP_VEXT3,
-    OP_VUZPL,  // VUZP, left result
-    OP_VUZPR,  // VUZP, right result
-    OP_VZIPL,  // VZIP, left result
-    OP_VZIPR,  // VZIP, right result
-    OP_VTRNL,  // VTRN, left result
-    OP_VTRNR,  // VTRN, right result
-    OP_MOVLANE // Move lane. RHSID is the lane to move into
-  };
-
-  if (OpNum == OP_COPY) {
-    if (LHSID == (1 * 9 + 2) * 9 + 3)
-      return LHS;
-    assert(LHSID == ((4 * 9 + 5) * 9 + 6) * 9 + 7 && "Illegal OP_COPY!");
-    return RHS;
-  }
-
-  if (OpNum == OP_MOVLANE) {
-    // Decompose a PerfectShuffle ID to get the Mask for lane Elt
-    auto getPFIDLane = [](unsigned ID, int Elt) -> int {
-      assert(Elt < 4 && "Expected Perfect Lanes to be less than 4");
-      Elt = 3 - Elt;
-      while (Elt > 0) {
-        ID /= 9;
-        Elt--;
-      }
-      return (ID % 9 == 8) ? -1 : ID % 9;
-    };
-
-    // For OP_MOVLANE shuffles, the RHSID represents the lane to move into. We
-    // get the lane to move from the PFID, which is always from the
-    // original vectors (V1 or V2).
-    SDValue OpLHS = GeneratePerfectShuffle(
-        LHSID, V1, V2, PerfectShuffleTable[LHSID], LHS, RHS, DAG, DL);
-    EVT VT = OpLHS.getValueType();
-    assert(RHSID < 8 && "Expected a lane index for RHSID!");
-    unsigned ExtLane = 0;
-    SDValue Input;
-
-    // OP_MOVLANE are either D movs (if bit 0x4 is set) or S movs. D movs
-    // convert into a higher type.
-    if (RHSID & 0x4) {
-      int MaskElt = getPFIDLane(ID, (RHSID & 0x01) << 1) >> 1;
-      if (MaskElt == -1)
-        MaskElt = (getPFIDLane(ID, ((RHSID & 0x01) << 1) + 1) - 1) >> 1;
-      assert(MaskElt >= 0 && "Didn't expect an undef movlane index!");
-      ExtLane = MaskElt < 2 ? MaskElt : (MaskElt - 2);
-      Input = MaskElt < 2 ? V1 : V2;
-      if (VT.getScalarSizeInBits() == 16) {
-        Input = DAG.getBitcast(MVT::v2f32, Input);
-        OpLHS = DAG.getBitcast(MVT::v2f32, OpLHS);
-      } else {
-        assert(VT.getScalarSizeInBits() == 32 &&
-               "Expected 16 or 32 bit shuffle elements");
-        Input = DAG.getBitcast(MVT::v2f64, Input);
-        OpLHS = DAG.getBitcast(MVT::v2f64, OpLHS);
-      }
-    } else {
-      int MaskElt = getPFIDLane(ID, RHSID);
-      assert(MaskElt >= 0 && "Didn't expect an undef movlane index!");
-      ExtLane = MaskElt < 4 ? MaskElt : (MaskElt - 4);
-      Input = MaskElt < 4 ? V1 : V2;
-      // Be careful about creating illegal types. Use f16 instead of i16.
-      if (VT == MVT::v4i16) {
-        Input = DAG.getBitcast(MVT::v4f16, Input);
-        OpLHS = DAG.getBitcast(MVT::v4f16, OpLHS);
-      }
-    }
-    SDValue Ext = DAG.getExtractVectorElt(
-        DL, Input.getValueType().getVectorElementType(), Input, ExtLane);
-    SDValue Ins = DAG.getInsertVectorElt(DL, OpLHS, Ext, RHSID & 0x3);
-    return DAG.getBitcast(VT, Ins);
-  }
-
-  SDValue OpLHS, OpRHS;
-  OpLHS = GeneratePerfectShuffle(LHSID, V1, V2, PerfectShuffleTable[LHSID], LHS,
-                                 RHS, DAG, DL);
-  OpRHS = GeneratePerfectShuffle(RHSID, V1, V2, PerfectShuffleTable[RHSID], LHS,
-                                 RHS, DAG, DL);
-  EVT VT = OpLHS.getValueType();
-
-  switch (OpNum) {
-  default:
-    llvm_unreachable("Unknown shuffle opcode!");
-  case OP_VREV: {
-    // VREV divides the vector in half and swaps within the half.
-    if (VT.getVectorElementType() == MVT::i32 ||
-        VT.getVectorElementType() == MVT::f32)
-      return DAG.getNode(AArch64ISD::REV64, DL, VT, OpLHS);
-    // vrev <4 x i16> -> REV32
-    if (VT.getVectorElementType() == MVT::i16 ||
-        VT.getVectorElementType() == MVT::f16 ||
-        VT.getVectorElementType() == MVT::bf16)
-      return DAG.getNode(AArch64ISD::REV32, DL, VT, OpLHS);
-    // vrev <4 x i8> -> BSWAP which is REV16
-    assert(VT == MVT::v8i8 || VT == MVT::v16i8);
-    EVT BSVT = VT == MVT::v8i8 ? MVT::v4i16 : MVT::v8i16;
-    return DAG.getNode(
-        AArch64ISD::NVCAST, DL, VT,
-        DAG.getNode(ISD::BSWAP, DL, BSVT,
-                    DAG.getNode(AArch64ISD::NVCAST, DL, BSVT, OpLHS)));
-  }
-  case OP_VDUP0:
-  case OP_VDUP1:
-  case OP_VDUP2:
-  case OP_VDUP3: {
-    EVT EltTy = VT.getVectorElementType();
-    unsigned Opcode;
-    if (EltTy == MVT::i8)
-      Opcode = AArch64ISD::DUPLANE8;
-    else if (EltTy == MVT::i16 || EltTy == MVT::f16 || EltTy == MVT::bf16)
-      Opcode = AArch64ISD::DUPLANE16;
-    else if (EltTy == MVT::i32 || EltTy == MVT::f32)
-      Opcode = AArch64ISD::DUPLANE32;
-    else if (EltTy == MVT::i64 || EltTy == MVT::f64)
-      Opcode = AArch64ISD::DUPLANE64;
-    else
-      llvm_unreachable("Invalid vector element type?");
-
-    if (VT.getSizeInBits() == 64)
-      OpLHS = WidenVector(OpLHS, DAG);
-    SDValue Lane = DAG.getConstant(OpNum - OP_VDUP0, DL, MVT::i64);
-    return DAG.getNode(Opcode, DL, VT, OpLHS, Lane);
-  }
-  case OP_VEXT1:
-  case OP_VEXT2:
-  case OP_VEXT3: {
-    unsigned Imm = (OpNum - OP_VEXT1 + 1) * getExtFactor(OpLHS);
-    return DAG.getNode(AArch64ISD::EXT, DL, VT, OpLHS, OpRHS,
-                       DAG.getConstant(Imm, DL, MVT::i32));
-  }
-  case OP_VUZPL:
-    return DAG.getNode(AArch64ISD::UZP1, DL, VT, OpLHS, OpRHS);
-  case OP_VUZPR:
-    return DAG.getNode(AArch64ISD::UZP2, DL, VT, OpLHS, OpRHS);
-  case OP_VZIPL:
-    return DAG.getNode(AArch64ISD::ZIP1, DL, VT, OpLHS, OpRHS);
-  case OP_VZIPR:
-    return DAG.getNode(AArch64ISD::ZIP2, DL, VT, OpLHS, OpRHS);
-  case OP_VTRNL:
-    return DAG.getNode(AArch64ISD::TRN1, DL, VT, OpLHS, OpRHS);
-  case OP_VTRNR:
-    return DAG.getNode(AArch64ISD::TRN2, DL, VT, OpLHS, OpRHS);
-  }
-}
-
 static SDValue GenerateTBL(SDValue Op, ArrayRef<int> ShuffleMask,
                            SelectionDAG &DAG) {
   // Check to see if we can use the TBL instruction.
@@ -16348,20 +16180,147 @@ SDValue AArch64TargetLowering::LowerVECTOR_SHUFFLE(SDValue Op,
   // If the shuffle is not directly supported and it has 4 elements, use
   // the PerfectShuffle-generated table to synthesize it from other shuffles.
   if (NumElts == 4) {
-    unsigned PFIndexes[4];
-    for (unsigned i = 0; i != 4; ++i) {
-      if (ShuffleMask[i] < 0)
-        PFIndexes[i] = 8;
-      else
-        PFIndexes[i] = ShuffleMask[i];
+    SmallVector<ShuffleEntry> Entries;
+    if (generatePerfectShuffle(ShuffleMask, NumElts, Entries)) {
+      SmallVector<SDValue> Vals;
+      auto getValue = [](unsigned Idx, SDValue LHS, SDValue RHS,
+                         SmallVector<SDValue> &Vals) {
+        if (Idx == ShuffleEntry::LHS)
+          return LHS;
+        if (Idx == ShuffleEntry::RHS)
+          return RHS;
+        assert(Idx < Vals.size());
+        return Vals[Idx];
+      };
+      for (const ShuffleEntry &Entry : Entries) {
+        SDValue OpLHS = getValue(Entry.LHSID, V1, V2, Vals);
+
+        switch (Entry.Op) {
+        case ShuffleEntry::OP_COPY:
+        case ShuffleEntry::OP_MOVLANE:
+          llvm_unreachable("Did not expect a OP_COPY or OP_MOVLANE");
+        case ShuffleEntry::OP_MOVLANE64: {
+          unsigned ExtLane = (Entry.RHSID >> 8) & 0xff;
+          unsigned ToLane = Entry.RHSID & 0xff;
+          bool Input2 = Entry.RHSID >> 16;
+
+          SDValue Input = Input2 ? V2 : V1;
+          if (VT.getScalarSizeInBits() == 16) {
+            Input = DAG.getBitcast(MVT::v2f32, Input);
+            OpLHS = DAG.getBitcast(MVT::v2f32, OpLHS);
+          } else {
+            assert(VT.getScalarSizeInBits() == 32 &&
+                   "Expected 16 or 32 bit shuffle elements");
+            Input = DAG.getBitcast(MVT::v2f64, Input);
+            OpLHS = DAG.getBitcast(MVT::v2f64, OpLHS);
+          }
+          SDValue Ext = DAG.getExtractVectorElt(
+              DL, Input.getValueType().getVectorElementType(), Input, ExtLane);
+          SDValue Ins = DAG.getInsertVectorElt(DL, OpLHS, Ext, ToLane);
+          Vals.push_back(DAG.getBitcast(VT, Ins));
+          break;
+        }
+        case ShuffleEntry::OP_MOVLANE32: {
+          unsigned ExtLane = (Entry.RHSID >> 8) & 0xff;
+          unsigned ToLane = Entry.RHSID & 0xff;
+          bool Input2 = Entry.RHSID >> 16;
+
+          SDValue Input = Input2 ? V2 : V1;
+          // Be careful about creating illegal types. Use f16 instead of i16.
+          if (VT == MVT::v4i16) {
+            Input = DAG.getBitcast(MVT::v4f16, Input);
+            OpLHS = DAG.getBitcast(MVT::v4f16, OpLHS);
+          }
+          SDValue Ext = DAG.getExtractVectorElt(
+              DL, Input.getValueType().getVectorElementType(), Input, ExtLane);
+          SDValue Ins = DAG.getInsertVectorElt(DL, OpLHS, Ext, ToLane);
+          Vals.push_back(DAG.getBitcast(VT, Ins));
+          break;
+        }
+        case ShuffleEntry::OP_VREV: {
+          // VREV divides the vector in half and swaps within the half.
+          if (VT.getVectorElementType() == MVT::i32 ||
+              VT.getVectorElementType() == MVT::f32)
+            Vals.push_back(DAG.getNode(AArch64ISD::REV64, DL, VT, OpLHS));
+          // vrev <4 x i16> -> REV32
+          else if (VT.getVectorElementType() == MVT::i16 ||
+                   VT.getVectorElementType() == MVT::f16 ||
+                   VT.getVectorElementType() == MVT::bf16)
+            Vals.push_back(DAG.getNode(AArch64ISD::REV32, DL, VT, OpLHS));
+          else {
+            // vrev <4 x i8> -> BSWAP which is REV16
+            assert(VT == MVT::v8i8 || VT == MVT::v16i8);
+            EVT BSVT = VT == MVT::v8i8 ? MVT::v4i16 : MVT::v8i16;
+            Vals.push_back(DAG.getNode(
+                AArch64ISD::NVCAST, DL, VT,
+                DAG.getNode(ISD::BSWAP, DL, BSVT,
+                            DAG.getNode(AArch64ISD::NVCAST, DL, BSVT, OpLHS))));
+          }
+          break;
+        }
+        case ShuffleEntry::OP_VDUP0:
+        case ShuffleEntry::OP_VDUP1:
+        case ShuffleEntry::OP_VDUP2:
+        case ShuffleEntry::OP_VDUP3: {
+          EVT EltTy = VT.getVectorElementType();
+          unsigned Opcode;
+          if (EltTy == MVT::i8)
+            Opcode = AArch64ISD::DUPLANE8;
+          else if (EltTy == MVT::i16 || EltTy == MVT::f16 || EltTy == MVT::bf16)
+            Opcode = AArch64ISD::DUPLANE16;
+          else if (EltTy == MVT::i32 || EltTy == MVT::f32)
+            Opcode = AArch64ISD::DUPLANE32;
+          else if (EltTy == MVT::i64 || EltTy == MVT::f64)
+            Opcode = AArch64ISD::DUPLANE64;
+          else
+            llvm_unreachable("Invalid vector element type?");
+
+          if (VT.getSizeInBits() == 64)
+            OpLHS = WidenVector(OpLHS, DAG);
+          SDValue Lane =
+              DAG.getConstant(Entry.Op - ShuffleEntry::OP_VDUP0, DL, MVT::i64);
+          Vals.push_back(DAG.getNode(Opcode, DL, VT, OpLHS, Lane));
+          break;
+        }
+        case ShuffleEntry::OP_VEXT1:
+        case ShuffleEntry::OP_VEXT2:
+        case ShuffleEntry::OP_VEXT3: {
+          unsigned Imm =
+              (Entry.Op - ShuffleEntry::OP_VEXT1 + 1) * getExtFactor(OpLHS);
+          Vals.push_back(DAG.getNode(AArch64ISD::EXT, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals),
+                                     DAG.getConstant(Imm, DL, MVT::i32)));
+          break;
+        }
+        case ShuffleEntry::OP_VUZPL:
+          Vals.push_back(DAG.getNode(AArch64ISD::UZP1, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        case ShuffleEntry::OP_VUZPR:
+          Vals.push_back(DAG.getNode(AArch64ISD::UZP2, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        case ShuffleEntry::OP_VZIPL:
+          Vals.push_back(DAG.getNode(AArch64ISD::ZIP1, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        case ShuffleEntry::OP_VZIPR:
+          Vals.push_back(DAG.getNode(AArch64ISD::ZIP2, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        case ShuffleEntry::OP_VTRNL:
+          Vals.push_back(DAG.getNode(AArch64ISD::TRN1, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        case ShuffleEntry::OP_VTRNR:
+          Vals.push_back(DAG.getNode(AArch64ISD::TRN2, DL, VT, OpLHS,
+                                     getValue(Entry.RHSID, V1, V2, Vals)));
+          break;
+        }
+      }
+      assert(Vals.size() == Entries.size());
+      return Vals.back();
     }
-
-    // Compute the index in the perfect shuffle table.
-    unsigned PFTableIndex = PFIndexes[0] * 9 * 9 * 9 + PFIndexes[1] * 9 * 9 +
-                            PFIndexes[2] * 9 + PFIndexes[3];
-    unsigned PFEntry = PerfectShuffleTable[PFTableIndex];
-    return GeneratePerfectShuffle(PFTableIndex, V1, V2, PFEntry, V1, V2, DAG,
-                                  DL);
   }
 
   // Check for a "select shuffle", generating a BSL to pick between lanes in
diff --git a/llvm/lib/Target/AArch64/AArch64PerfectShuffle.h b/llvm/lib/Target/AArch64/AArch64PerfectShuffle.h
index 00d9edbbc02ea..3f9c9558d04b3 100644
--- a/llvm/lib/Target/AArch64/AArch64PerfectShuffle.h
+++ b/llvm/lib/Target/AArch64/AArch64PerfectShuffle.h
@@ -305,6 +305,125 @@ inline bool isDUPFirstSegmentMask(ArrayRef<int> Mask, unsigned Segments,
   });
 }
 
+/// ShuffleEntry - Represent a shuffle entry in the decomposion of a vector
+/// shuffle. i.e. a vector shuffle LHS, RHS, Mask can be built using a list of
+/// ShuffleEntry, by performing Op to either LHS, RHS or one of the previous
+/// ShuffleEntrys in the lists.
+struct ShuffleEntry {
+  // The supported operations. The first 16 match those generated by
+  // PerfectShuffle.cpp.
+  enum Operation {
+    OP_COPY = 0, // Copy, used for things like <u,u,u,3> to say it is <0,1,2,3>
+    OP_VREV,
+    OP_VDUP0,
+    OP_VDUP1,
+    OP_VDUP2,
+    OP_VDUP3,
+    OP_VEXT1,
+    OP_VEXT2,
+    OP_VEXT3,
+    OP_VUZPL,   // VUZP, left result
+    OP_VUZPR,   // VUZP, right result
+    OP_VZIPL,   // VZIP, left result
+    OP_VZIPR,   // VZIP, right result
+    OP_VTRNL,   // VTRN, left result
+    OP_VTRNR,   // VTRN, right result
+    OP_MOVLANE, // Move lane. RHSID is the lane to move into
+
+    OP_MOVLANE32,
+    OP_MOVLANE64,
+  };
+
+  /// Special IDs for the LHS and RHS values.
+  enum IDs {
+    LHS = 0xfe,
+    RHS = 0xff,
+  };
+
+  Operation Op;
+  unsigned LHSID;
+  unsigned RHSID;
+};
+
+inline unsigned
+generatePerfectShuffleFromTable(unsigned PFTableIndex,
+                                SmallVector<ShuffleEntry> &Entries) {
+  unsigned PFEntry = PerfectShuffleTable[PFTableIndex];
+  ShuffleEntry::Operation OpNum =
+      (ShuffleEntry::Operation)((PFEntry >> 26) & 0x0F);
+  unsigned LHSID = (PFEntry >> 13) & ((1 << 13) - 1);
+  unsigned RHSID = (PFEntry >> 0) & ((1 << 13) - 1);
+
+  if (OpNum == ShuffleEntry::OP_COPY) {
+    if (LHSID == (1 * 9 + 2) * 9 + 3)
+      return ShuffleEntry::LHS;
+    assert(LHSID == ((4 * 9 + 5) * 9 + 6) * 9 + 7 && "Illegal OP_COPY!");
+    return ShuffleEntry::RHS;
+  }
+
+  unsigned OpLHS = generatePerfectShuffleFromTable(LHSID, Entries);
+
+  if (OpNum == ShuffleEntry::OP_MOVLANE) {
+    // Decompose a PerfectShuffle ID to get the Mask for lane Elt
+    auto getPFIDLane = [](unsigned ID, int Elt) -> int {
+      assert(Elt < 4 && "Expected Perfect Lanes to be less than 4");
+      Elt = 3 - Elt;
+      while (Elt > 0) {
+        ID /= 9;
+        Elt--;
+      }
+      return (ID % 9 == 8) ? -1 : ID % 9;
+    };
+
+    // OP_MOVLANE are either D movs (if bit 0x4 is set) or S movs. D movs
+    // convert into a higher type.
+    if (RHSID & 0x4) {
+      int MaskElt = getPFIDLane(PFTableIndex, (RHSID & 0x01) << 1) >> 1;
+      if (MaskElt == -1)
+        MaskElt =
+            (getPFIDLane(PFTableIndex, ((RHSID & 0x01) << 1) + 1) - 1) >> 1;
+      assert(MaskElt >= 0 && "Didn't expect an undef movlane index!");
+      unsigned ExtLane = MaskElt < 2 ? MaskElt : (MaskElt - 2);
+      Entries.push_back({ShuffleEntry::OP_MOVLANE64, OpLHS,
+                         (MaskElt >= 2) << 16 | ExtLane << 8 | (RHSID & 0x3)});
+    } else {
+      int MaskElt = getPFIDLane(PFTableIndex, RHSID);
+      assert(MaskElt >= 0 && "Didn't expect an undef movlane index!");
+      unsigned ExtLane = MaskElt < 4 ? MaskElt : (MaskElt - 4);
+      Entries.push_back({ShuffleEntry::OP_MOVLANE32, OpLHS,
+                         (MaskElt >= 4) << 16 | ExtLane << 8 | (RHSID & 0x3)});
+    }
+    return Entries.size() - 1;
+  }
+
+  unsigned OpRHS = generatePerfectShuffleFromTable(RHSID, Entries);
+
+  Entries.push_back({OpNum, OpLHS, OpRHS});
+  return Entries.size() - 1;
+}
+
+/// generatePerfectShuffle - Given a Mask, attempt to generate the optimal
+/// sequence of instructions using zip/uzp/trn/dup/etc. Currently uses the
+/// perfect shuffle tables.
+inline bool generatePerfectShuffle(ArrayRef<int> Mask, unsigned NumElts,
+                                   SmallVector<ShuffleEntry> &Entries) {
+  assert(NumElts == 4 && "Only 4 element masks supported at the moment");
+
+  // Compute the index in the perfect shuffle table.
+  unsigned PFIndexes[4];
+  for (unsigned i = 0; i != 4; ++i) {
+    if (Mask[i] < 0)
+      PFIndexes[i] = 8;
+    else
+      PFIndexes[i] = Mask[i];
+  }
+
+  unsigned PFTableIndex = PFIndexes[0] * 9 * 9 * 9 + PFIndexes[1] * 9 * 9 +
+                          PFIndexes[2] * 9 + PFIndexes[3];
+  unsigned Idx = generatePerfectShuffleFromTable(PFTableIndex, Entries);
+  return Idx == Entries.size() - 1;
+}
+
 } // namespace llvm
 
 #endif



More information about the llvm-commits mailing list