[llvm] Aarch64 extract movz movk only subtarget feature (PR #216166)

Tomer Shafir via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 13:05:56 PDT 2026


https://github.com/tomershafir created https://github.com/llvm/llvm-project/pull/216166

Depends on https://github.com/llvm/llvm-project/pull/214849. The first 5 commits are from the base PR.

>From 9a1c1c68f4c3284498fced1767500160f579a6e0 Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Fri, 7 Aug 2026 09:56:26 +0300
Subject: [PATCH 1/5] [AArch64] Restrict FP imm ISel by accurate subtarget
 macro-fusion

This patch improves the accuracy of FP immediate lowering for runtime performance builds. Until now we relaxed the instruction count limit from 2 to 4 merely based on the satisfaction of `ST.hasFuseLiterals`, but this could be wrong for example false-positive for `MOVN` instructions which would relax the limit but are not macro fused. Here we check exactly if immediate materialization parts can be macro fus
ed using a new subtarget helper which is shared with macro-fusion.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    |  22 +-
 .../lib/Target/AArch64/AArch64MacroFusion.cpp |  28 +-
 llvm/lib/Target/AArch64/AArch64Subtarget.cpp  |  47 ++
 llvm/lib/Target/AArch64/AArch64Subtarget.h    |  11 +
 .../fpimm-legal-expand-fuse-literals.ll       | 589 ++++++++++++++++++
 .../AArch64/fpimm-legal-expand-optsize.ll     | 117 ++++
 6 files changed, 787 insertions(+), 27 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
 create mode 100644 llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 51be0e66b19b0..be3d337358d53 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -13756,7 +13756,6 @@ bool AArch64TargetLowering::isFPImmLegalAsFMov(const APFloat &Imm,
 bool AArch64TargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
                                          bool OptForSize) const {
   bool IsLegal = isFPImmLegalAsFMov(Imm, VT);
-  const APInt ImmInt = Imm.bitcastToAPInt();
 
   // If we can not materialize in immediate field for fmov, check if the
   // value can be encoded as the immediate operand of a logical instruction.
@@ -13771,15 +13770,32 @@ bool AArch64TargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
     // movw+movk is fused). So by default we limit up to 2 instructions
     // or 4 with hasFuseLiterals.
     SmallVector<AArch64_IMM::ImmInsnModel, 4> Insn;
+    const APInt ImmInt = Imm.bitcastToAPInt();
     AArch64_IMM::expandMOVImm(ImmInt.getZExtValue(), VT.getSizeInBits(), Insn);
     assert(Insn.size() <= 4 &&
            "Should be able to build any value with at most 4 moves");
-    unsigned Limit = (OptForSize ? 1 : (Subtarget->hasFuseLiterals() ? 4 : 2));
+
+    unsigned Limit = OptForSize ? 1 : 2;
+
+    if (!OptForSize && Insn.size() > Limit && Subtarget->hasFuseLiterals()) {
+      // Relax the limit based on subtarget fusion capabilites
+      for (unsigned i = 0; i + 1 < Insn.size(); ++i) {
+        if (Subtarget->fusesMOVImmPair(Insn[i].Opcode, Insn[i].Op2,
+                                       Insn[i + 1].Opcode, Insn[i + 1].Op2)) {
+          ++Limit;
+          // An instruction can only be fused once, so the 2nd one of the pair
+          // cannot start another pair and is skipped.
+          ++i;
+        }
+      }
+    }
+
     IsLegal = Insn.size() <= Limit;
   }
 
   LLVM_DEBUG(dbgs() << (IsLegal ? "Legal " : "Illegal ") << VT
-                    << " imm value: "; Imm.dump(););
+                    << " imm value: ";
+             Imm.dump(););
   return IsLegal;
 }
 
diff --git a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
index c367b65cd28f9..d5c669c14b296 100644
--- a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
@@ -224,30 +224,10 @@ static bool isAdrpAddPair(const MachineInstr *FirstMI,
 }
 
 /// Literal generation.
-static bool isLiteralsPair(const MachineInstr *FirstMI,
+static bool isLiteralsPair(const AArch64Subtarget &ST,
+                           const MachineInstr *FirstMI,
                            const MachineInstr &SecondMI) {
-  // Assume the 1st instr to be a wildcard if it is unspecified.
-  // 32 bit immediate.
-  if ((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZWi) &&
-      (SecondMI.getOpcode() == AArch64::MOVKWi &&
-       SecondMI.getOperand(3).getImm() == 16))
-    return true;
-
-  // Lower half of 64 bit immediate.
-  if((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZXi) &&
-     (SecondMI.getOpcode() == AArch64::MOVKXi &&
-      SecondMI.getOperand(3).getImm() == 16))
-    return true;
-
-  // Upper half of 64 bit immediate.
-  if ((FirstMI == nullptr ||
-       (FirstMI->getOpcode() == AArch64::MOVKXi &&
-        FirstMI->getOperand(3).getImm() == 32)) &&
-      (SecondMI.getOpcode() == AArch64::MOVKXi &&
-       SecondMI.getOperand(3).getImm() == 48))
-    return true;
-
-  return false;
+  return ST.fusesMOVImmPair(FirstMI, SecondMI);
 }
 
 /// Fuse address generation and loads or stores.
@@ -714,7 +694,7 @@ static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
     ++NumFusedAdrpAdd;
     return true;
   }
-  if (ST.hasFuseLiterals() && isLiteralsPair(FirstMI, SecondMI)) {
+  if (ST.hasFuseLiterals() && isLiteralsPair(ST, FirstMI, SecondMI)) {
     ++NumFusedLiterals;
     return true;
   }
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
index 9ee3d0cc9e0ea..4fef12ab37ad8 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
@@ -651,3 +651,50 @@ bool AArch64Subtarget::isX16X17Safer() const {
 bool AArch64Subtarget::enableMachinePipeliner() const {
   return getSchedModel().hasInstrSchedModel();
 }
+
+/// Returns a MOVK's shifter operand, or 0 otherwise.
+static unsigned getMOVKShiftImm(const MachineInstr &MI) {
+  unsigned Opc = MI.getOpcode();
+  if (Opc != AArch64::MOVKWi && Opc != AArch64::MOVKXi)
+    return 0;
+  return MI.getOperand(3).getImm();
+}
+
+/// \p HasFirst is false when the 1st instruction is a wildcard.
+static bool fusesMOVImmPairImpl(const AArch64Subtarget &ST, bool HasFirst,
+                                unsigned FirstOpc, unsigned FirstShift,
+                                unsigned SecondOpc, unsigned SecondShift) {
+  assert(ST.hasFuseLiterals() && "the subtarget doesn't fuse move immediate");
+
+  // 32 bit immediate.
+  if ((!HasFirst || FirstOpc == AArch64::MOVZWi) &&
+      SecondOpc == AArch64::MOVKWi && SecondShift == 16)
+    return true;
+
+  // Lower half of 64 bit immediate.
+  if ((!HasFirst || FirstOpc == AArch64::MOVZXi) &&
+      SecondOpc == AArch64::MOVKXi && SecondShift == 16)
+    return true;
+
+  // Upper half of 64 bit immediate.
+  if ((!HasFirst || (FirstOpc == AArch64::MOVKXi && FirstShift == 32)) &&
+      SecondOpc == AArch64::MOVKXi && SecondShift == 48)
+    return true;
+
+  return false;
+}
+
+bool AArch64Subtarget::fusesMOVImmPair(unsigned FirstOpc, unsigned FirstShift,
+                                       unsigned SecondOpc,
+                                       unsigned SecondShift) const {
+  return fusesMOVImmPairImpl(*this, /*HasFirst=*/true, FirstOpc, FirstShift,
+                             SecondOpc, SecondShift);
+}
+
+bool AArch64Subtarget::fusesMOVImmPair(const MachineInstr *FirstMI,
+                                       const MachineInstr &SecondMI) const {
+  return fusesMOVImmPairImpl(*this, FirstMI != nullptr,
+                             FirstMI ? FirstMI->getOpcode() : 0,
+                             FirstMI ? getMOVKShiftImm(*FirstMI) : 0,
+                             SecondMI.getOpcode(), getMOVKShiftImm(SecondMI));
+}
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h
index 380c3e11fcbf2..98cc97ae0a695 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.h
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h
@@ -268,6 +268,17 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
            hasFuseLiterals() || hasFuseAppleSMECompute() || hasFuseFMinFMax();
   }
 
+  /// Return true if the subtarget fuses this pair of move immediate
+  /// instructions.
+  bool fusesMOVImmPair(unsigned FirstOpc, unsigned FirstShift,
+                       unsigned SecondOpc, unsigned SecondShift) const;
+
+  /// Return true if the subtarget fuses this pair of move immediate
+  /// instructions. The 1st instruction is a wildcard when it is nullptr, which
+  /// tells whether the 2nd one can be fused at all.
+  bool fusesMOVImmPair(const MachineInstr *FirstMI,
+                       const MachineInstr &SecondMI) const;
+
   unsigned getEpilogueVectorizationMinVF() const {
     return EpilogueVectorizationMinVF;
   }
diff --git a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
new file mode 100644
index 0000000000000..3afc99254976c
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
@@ -0,0 +1,589 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-literals | FileCheck %s --check-prefixes=CHECK,FUSE
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=-fuse-literals | FileCheck %s --check-prefixes=CHECK,NOFUSE
+
+define double @movz() {
+; CHECK-LABEL: movz:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000000001234
+}
+
+define double @movz16() {
+; CHECK-LABEL: movz16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000012340000
+}
+
+define double @movz32() {
+; CHECK-LABEL: movz32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #20014547599360 // =0x123400000000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000123400000000
+}
+
+define double @movz48() {
+; CHECK-LABEL: movz48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1311673391471656960 // =0x1234000000000000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234000000000000
+}
+
+define double @movn() {
+; CHECK-LABEL: movn:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFFFFFF1234
+}
+
+define double @movn16() {
+; CHECK-LABEL: movn16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFF1234FFFF
+}
+
+define double @movn32() {
+; CHECK-LABEL: movn32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-261456134144001 // =0xffff1234ffffffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF1234FFFFFFFF
+}
+
+define double @movn48() {
+; CHECK-LABEL: movn48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1311954866448367615 // =0x1234ffffffffffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234FFFFFFFFFFFF
+}
+
+define double @orr() {
+; CHECK-LABEL: orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000100010001
+}
+
+define double @movz_movk16() {
+; CHECK-LABEL: movz_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000056781234
+}
+
+define double @movz_movk32() {
+; CHECK-LABEL: movz_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000567800001234
+}
+
+define double @movz_movk48() {
+; CHECK-LABEL: movz_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678000000001234
+}
+
+define double @movz16_movk32() {
+; CHECK-LABEL: movz16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000567812340000
+}
+
+define double @movz16_movk48() {
+; CHECK-LABEL: movz16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678000012340000
+}
+
+define double @movz32_movk48() {
+; CHECK-LABEL: movz32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #20014547599360 // =0x123400000000
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678123400000000
+}
+
+define double @movn_movk16() {
+; CHECK-LABEL: movn_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFF56781234
+}
+
+define double @movn_movk32() {
+; CHECK-LABEL: movn_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF5678FFFF1234
+}
+
+define double @movn_movk48() {
+; CHECK-LABEL: movn_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678FFFFFFFF1234
+}
+
+define double @movn16_movk32() {
+; CHECK-LABEL: movn16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF56781234FFFF
+}
+
+define double @movn16_movk48() {
+; CHECK-LABEL: movn16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678FFFF1234FFFF
+}
+
+define double @movn32_movk48() {
+; CHECK-LABEL: movn32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-261456134144001 // =0xffff1234ffffffff
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x56781234FFFFFFFF
+}
+
+define double @movn_eors() {
+; CHECK-LABEL: movn_eors:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-4661 // =0xffffffffffffedcb
+; CHECK-NEXT:    eor x8, x8, x8, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x00001234FFFFEDCB
+}
+
+define double @movn_eons() {
+; CHECK-LABEL: movn_eons:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-4661 // =0xffffffffffffedcb
+; CHECK-NEXT:    eon x8, x8, x8, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFEDCB00001234
+}
+
+define double @orr_eor() {
+; CHECK-LABEL: orr_eor:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #562945658585087 // =0x1ffff0001ffff
+; CHECK-NEXT:    eor x8, x8, #0x3fffe
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001FFFF00020001
+}
+
+define double @orr_movk0() {
+; CHECK-LABEL: orr_movk0:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000100011234
+}
+
+define double @orr_movk16() {
+; CHECK-LABEL: orr_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000112340001
+}
+
+define double @orr_movk32() {
+; CHECK-LABEL: orr_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001123400010001
+}
+
+define double @orr_movk48() {
+; CHECK-LABEL: orr_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234000100010001
+}
+
+define double @orr_orr() {
+; CHECK-LABEL: orr_orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1125917086973956 // =0x4000400040004
+; CHECK-NEXT:    orr x8, x8, #0x1000100010001
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0005000500050005
+}
+
+define double @orr_and() {
+; CHECK-LABEL: orr_and:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #6148914691236517205 // =0x5555555555555555
+; CHECK-NEXT:    and x8, x8, #0xff00ff00ff00ff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0055005500550055
+}
+
+define double @movz_movk16_movk32() {
+; FUSE-LABEL: movz_movk16_movk32:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #16
+; FUSE-NEXT:    movk x8, #39612, lsl #32
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_movk32:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI30_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI30_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x00009ABC56781234
+}
+
+define double @movz_movk16_movk48() {
+; FUSE-LABEL: movz_movk16_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #16
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI31_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI31_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC000056781234
+}
+
+define double @movz_movk16_orrs() {
+; FUSE-LABEL: movz_movk16_orrs:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #4660, lsl #16
+; FUSE-NEXT:    orr x8, x8, x8, lsl #32
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_orrs:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI32_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI32_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x1234123412341234
+}
+
+define double @movz_movk32_movk48() {
+; FUSE-LABEL: movz_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI33_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI33_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC567800001234
+}
+
+define double @movz16_movk32_movk48() {
+; FUSE-LABEL: movz16_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #305397760 // =0x12340000
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz16_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI34_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI34_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC567812340000
+}
+
+define double @movn_movk16_eors() {
+; CHECK-LABEL: movn_movk16_eors:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI35_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI35_0]
+; CHECK-NEXT:    ret
+  ret double 0xA987EDCB56781234
+}
+
+define double @movn_movk16_movk32() {
+; CHECK-LABEL: movn_movk16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI36_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI36_0]
+; CHECK-NEXT:    ret
+  ret double 0xFFFF9ABC56781234
+}
+
+define double @movn_movk16_movk48() {
+; CHECK-LABEL: movn_movk16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI37_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI37_0]
+; CHECK-NEXT:    ret
+  ret double 0x9ABCFFFF56781234
+}
+
+define double @movn_movk32_movk48() {
+; FUSE-LABEL: movn_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movn_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI38_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI38_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC5678FFFF1234
+}
+
+define double @movn16_movk32_movk48() {
+; FUSE-LABEL: movn16_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movn16_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI39_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI39_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC56781234FFFF
+}
+
+define double @orr_movk0_movk16() {
+; CHECK-LABEL: orr_movk0_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI40_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI40_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001000156781234
+}
+
+define double @orr_movk0_movk32() {
+; CHECK-LABEL: orr_movk0_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI41_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI41_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001567800011234
+}
+
+define double @orr_movk0_movk48() {
+; CHECK-LABEL: orr_movk0_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI42_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI42_0]
+; CHECK-NEXT:    ret
+  ret double 0x5678000100011234
+}
+
+define double @orr_movk16_movk32() {
+; CHECK-LABEL: orr_movk16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI43_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI43_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001567812340001
+}
+
+define double @orr_movk16_movk48() {
+; CHECK-LABEL: orr_movk16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI44_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI44_0]
+; CHECK-NEXT:    ret
+  ret double 0x5678000112340001
+}
+
+define double @orr_movk32_movk48() {
+; FUSE-LABEL: orr_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; FUSE-NEXT:    movk x8, #4660, lsl #32
+; FUSE-NEXT:    movk x8, #22136, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: orr_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI45_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI45_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x5678123400010001
+}
+
+define double @movn_movk16_eons() {
+; CHECK-LABEL: movn_movk16_eons:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI46_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI46_0]
+; CHECK-NEXT:    ret
+  ret double 0xFFF567818AC7EDCB
+}
+
+define double @movz_movk16_movk32_movk48() {
+; FUSE-LABEL: movz_movk16_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #16
+; FUSE-NEXT:    movk x8, #39612, lsl #32
+; FUSE-NEXT:    movk x8, #43981, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI47_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI47_0]
+; NOFUSE-NEXT:    ret
+  ret double 0xABCD9ABC56781234
+}
+
+;; 32 bit FP immediate expansion shouldnt be affected by the limit,
+;; because we can always materialize it using up to 2 instructions - as
+;; `AArch64ExpandImm.cpp` asserts, which is already the default limit
+;; for non-optsize functions.
+
+define float @f32_movz() {
+; CHECK-LABEL: f32_movz:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 4660 to float)
+}
+
+define float @f32_movz16() {
+; CHECK-LABEL: f32_movz16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #305397760 // =0x12340000
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 305397760 to float)
+}
+
+define float @f32_movn() {
+; CHECK-LABEL: f32_movn:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #-60876 // =0xffff1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 -60876 to float)
+}
+
+define float @f32_movn16() {
+; CHECK-LABEL: f32_movn16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #305463295 // =0x1234ffff
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 305463295 to float)
+}
+
+define float @f32_orr() {
+; CHECK-LABEL: f32_orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #16843009 // =0x1010101
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 16843009 to float)
+}
+
+define float @f32_movz_movk16() {
+; CHECK-LABEL: f32_movz_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    movk w8, #3855, lsl #16
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 252645940 to float)
+}
diff --git a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll
new file mode 100644
index 0000000000000..61604b5a13e27
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll
@@ -0,0 +1,117 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-literals | FileCheck %s
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=-fuse-literals | FileCheck %s
+
+;; Representative tests for each class of FP immediate expansion that shouldn't apply
+;; when optimizing for size.
+
+define double @movz_optsize() #0 {
+; CHECK-LABEL: movz_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000000001234
+}
+
+define double @movz_minsize() #1 {
+; CHECK-LABEL: movz_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000000001234
+}
+
+define double @movz_movk16_optsize() #0 {
+; CHECK-LABEL: movz_movk16_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI2_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI2_0]
+; CHECK-NEXT:    ret
+  ret double 0x0000000056781234
+}
+
+define double @movz_movk16_minsize() #1 {
+; CHECK-LABEL: movz_movk16_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI3_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI3_0]
+; CHECK-NEXT:    ret
+  ret double 0x0000000056781234
+}
+
+define double @movz_movk16_movk32_optsize() #0 {
+; CHECK-LABEL: movz_movk16_movk32_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI4_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI4_0]
+; CHECK-NEXT:    ret
+  ret double 0x00009ABC56781234
+}
+
+define double @movz_movk16_movk32_minsize() #1 {
+; CHECK-LABEL: movz_movk16_movk32_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI5_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI5_0]
+; CHECK-NEXT:    ret
+  ret double 0x00009ABC56781234
+}
+
+define double @movz_movk16_movk32_movk48_optsize() #0 {
+; CHECK-LABEL: movz_movk16_movk32_movk48_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI6_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI6_0]
+; CHECK-NEXT:    ret
+  ret double 0xABCD9ABC56781234
+}
+
+define double @movz_movk16_movk32_movk48_minsize() #1 {
+; CHECK-LABEL: movz_movk16_movk32_movk48_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI7_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI7_0]
+; CHECK-NEXT:    ret
+  ret double 0xABCD9ABC56781234
+}
+
+define float @f32_movz_optsize() #0 {
+; CHECK-LABEL: f32_movz_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 4660 to float)
+}
+
+define float @f32_movz_minsize() #1 {
+; CHECK-LABEL: f32_movz_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 4660 to float)
+}
+
+define float @f32_movz_movk16_optsize() #0 {
+; CHECK-LABEL: f32_movz_movk16_optsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI10_0
+; CHECK-NEXT:    ldr s0, [x8, :lo12:.LCPI10_0]
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 252645940 to float)
+}
+
+define float @f32_movz_movk16_minsize() #1 {
+; CHECK-LABEL: f32_movz_movk16_minsize:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI11_0
+; CHECK-NEXT:    ldr s0, [x8, :lo12:.LCPI11_0]
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 252645940 to float)
+}
+
+attributes #0 = { optsize }
+attributes #1 = { minsize optsize }

>From c378332621c18439e22244d64c1eaa402112a0bf Mon Sep 17 00:00:00 2001
From: Tomer Shafir <tomer.shafir8 at gmail.com>
Date: Tue, 11 Aug 2026 21:12:41 +0300
Subject: [PATCH 2/5] Apply suggestion from @jroelofs

Co-authored-by: Jon Roelofs <jroelofs at gmail.com>
---
 llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
index 3afc99254976c..d38d7ac299921 100644
--- a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
+++ b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-literals.ll
@@ -528,7 +528,7 @@ define double @movz_movk16_movk32_movk48() {
   ret double 0xABCD9ABC56781234
 }
 
-;; 32 bit FP immediate expansion shouldnt be affected by the limit,
+;; 32 bit FP immediate expansion shouldn't be affected by the limit,
 ;; because we can always materialize it using up to 2 instructions - as
 ;; `AArch64ExpandImm.cpp` asserts, which is already the default limit
 ;; for non-optsize functions.

>From 6ac05bb5cdc376d436a9f85d814267443b23d9b4 Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Tue, 11 Aug 2026 21:12:06 +0300
Subject: [PATCH 3/5] inline isLiteralsPair

---
 llvm/lib/Target/AArch64/AArch64MacroFusion.cpp | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
index d5c669c14b296..b914945043495 100644
--- a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
@@ -223,13 +223,6 @@ static bool isAdrpAddPair(const MachineInstr *FirstMI,
   return false;
 }
 
-/// Literal generation.
-static bool isLiteralsPair(const AArch64Subtarget &ST,
-                           const MachineInstr *FirstMI,
-                           const MachineInstr &SecondMI) {
-  return ST.fusesMOVImmPair(FirstMI, SecondMI);
-}
-
 /// Fuse address generation and loads or stores.
 static bool isAddressLdStPair(const MachineInstr *FirstMI,
                               const MachineInstr &SecondMI) {
@@ -694,7 +687,7 @@ static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
     ++NumFusedAdrpAdd;
     return true;
   }
-  if (ST.hasFuseLiterals() && isLiteralsPair(ST, FirstMI, SecondMI)) {
+  if (ST.hasFuseLiterals() && ST.fusesMOVImmPair(FirstMI, SecondMI)) {
     ++NumFusedLiterals;
     return true;
   }

>From 4a5ad58c7530108c4d93ccc8f62b3eb8702bfed9 Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Wed, 12 Aug 2026 11:14:08 +0300
Subject: [PATCH 4/5] hoist pair defs and fix loop var name

---
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index be3d337358d53..e59af42784fa7 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -13779,13 +13779,15 @@ bool AArch64TargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
 
     if (!OptForSize && Insn.size() > Limit && Subtarget->hasFuseLiterals()) {
       // Relax the limit based on subtarget fusion capabilites
-      for (unsigned i = 0; i + 1 < Insn.size(); ++i) {
-        if (Subtarget->fusesMOVImmPair(Insn[i].Opcode, Insn[i].Op2,
-                                       Insn[i + 1].Opcode, Insn[i + 1].Op2)) {
+      for (unsigned I = 0; I + 1 < Insn.size(); ++I) {
+        const AArch64_IMM::ImmInsnModel &First = Insn[I];
+        const AArch64_IMM::ImmInsnModel &Second = Insn[I + 1];
+        if (Subtarget->fusesMOVImmPair(First.Opcode, First.Op2, Second.Opcode,
+                                       Second.Op2)) {
           ++Limit;
           // An instruction can only be fused once, so the 2nd one of the pair
           // cannot start another pair and is skipped.
-          ++i;
+          ++I;
         }
       }
     }

>From 9d462f8007be8796186566b7a3f58e60dc298dda Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Thu, 13 Aug 2026 23:02:57 +0300
Subject: [PATCH 5/5] [AArch64] Extract MOVZ+MOVK only clustering

This patch extracts a subtarget feature out of wider `FuseLiterals` that controls scheduling MOVZ+MOVK instructions only back to back. Enabled on Apple CPU.
---
 llvm/lib/Target/AArch64/AArch64Features.td    |    5 +
 .../Target/AArch64/AArch64ISelLowering.cpp    |   16 +-
 .../lib/Target/AArch64/AArch64MacroFusion.cpp |   23 +
 llvm/lib/Target/AArch64/AArch64Processors.td  |    2 +-
 llvm/lib/Target/AArch64/AArch64Subtarget.cpp  |   34 +
 llvm/lib/Target/AArch64/AArch64Subtarget.h    |   19 +-
 .../fpimm-legal-expand-fuse-movz-movk.ll      |  556 ++++++++
 .../AArch64/fpimm-legal-expand-optsize.ll     |    2 +
 .../AArch64/literal_pools_float_apple.ll      |   10 +-
 .../misched-fusion-movz-movk-post-ra.mir      | 1124 +++++++++++++++++
 .../misched-fusion-movz-movk-pre-ra.mir       |  584 +++++++++
 .../AArch64/misched-fusion-movz-movk.ll       |   60 +
 .../TableGen/aarch64-apple-tuning-features.td |   12 +-
 13 files changed, 2430 insertions(+), 17 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-movz-movk.ll
 create mode 100644 llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-post-ra.mir
 create mode 100644 llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-pre-ra.mir
 create mode 100644 llvm/test/CodeGen/AArch64/misched-fusion-movz-movk.ll

diff --git a/llvm/lib/Target/AArch64/AArch64Features.td b/llvm/lib/Target/AArch64/AArch64Features.td
index 6eea064b257f6..6c36b881f72d3 100644
--- a/llvm/lib/Target/AArch64/AArch64Features.td
+++ b/llvm/lib/Target/AArch64/AArch64Features.td
@@ -853,6 +853,11 @@ def FeatureFuseLiterals : SubtargetFeature<
     "CPU fuses literal generation operations",
     [], InlineIgnore>;
 
+def FeatureFuseMovzMovk : SubtargetFeature<
+    "fuse-movz-movk", "HasFuseMovzMovk", "true",
+    "CPU fuses MOVZ and MOVK operations",
+    [], InlineIgnore>;
+
 def FeatureFuseAddSub2RegAndConstOne : SubtargetFeature<
    "fuse-addsub-2reg-const1", "HasFuseAddSub2RegAndConstOne", "true",
    "CPU fuses (a + b + 1) and (a - b - 1)",
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 64e09f6962d29..02a74a3636771 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -13862,13 +13862,21 @@ bool AArch64TargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT,
 
     unsigned Limit = OptForSize ? 1 : 2;
 
-    if (!OptForSize && Insn.size() > Limit && Subtarget->hasFuseLiterals()) {
-      // Relax the limit based on subtarget fusion capabilites
+    if (!OptForSize && Insn.size() > Limit) {
+      // Relax the limit based on subtarget fusion capabilites.
+      //
+      // The dependencies that macro-fusion requires hold by construction here.
+      // AArch64ExpandPseudoInsts writes the pseudo's dest throughout the
+      // expansion, which gives WAW, and the 2nd instruction of a fused pair is
+      // always a MOVK, whose source is tied to its dest, which gives RAW.
       for (unsigned I = 0; I + 1 < Insn.size(); ++I) {
         const AArch64_IMM::ImmInsnModel &First = Insn[I];
         const AArch64_IMM::ImmInsnModel &Second = Insn[I + 1];
-        if (Subtarget->fusesMOVImmPair(First.Opcode, First.Op2, Second.Opcode,
-                                       Second.Op2)) {
+        if ((Subtarget->hasFuseLiterals() &&
+             Subtarget->fusesMOVImmPair(First.Opcode, First.Op2, Second.Opcode,
+                                        Second.Op2)) ||
+            (Subtarget->hasFuseMovzMovk() &&
+             Subtarget->fusesMovzMovkPair(First.Opcode, Second.Opcode))) {
           ++Limit;
           // An instruction can only be fused once, so the 2nd one of the pair
           // cannot start another pair and is skipped.
diff --git a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
index 276855beb6aa8..09d6f952093f4 100644
--- a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
@@ -27,6 +27,7 @@ STATISTIC(NumFusedAES, "Number of AES fusions");
 STATISTIC(NumFusedCryptoEOR, "Number of crypto-EOR fusions");
 STATISTIC(NumFusedAdrpAdd, "Number of ADRP-ADD fusions");
 STATISTIC(NumFusedLiterals, "Number of literal-generation fusions");
+STATISTIC(NumFusedMovzMovk, "Number of MOVZ-MOVK fusions");
 STATISTIC(NumFusedAddress, "Number of address-generation load/store fusions");
 STATISTIC(NumFusedCmpCSel, "Number of compare-CSEL fusions");
 STATISTIC(NumFusedFCmpFCSel, "Number of FP-compare-FCSEL fusions");
@@ -643,6 +644,24 @@ static bool isFMinFMaxPair(const MachineInstr *FirstMI,
   return mayHaveWAWDependency(*FirstMI, SecondMI, TRI);
 }
 
+// MOVZ + MOVK.
+static bool isMovzMovkPair(const MachineInstr *FirstMI,
+                           const MachineInstr &SecondMI,
+                           const TargetRegisterInfo *TRI,
+                           const AArch64Subtarget &ST) {
+  if (!ST.fusesMovzMovkPair(FirstMI, SecondMI))
+    return false;
+
+  // The opcode check above only establishes the shape of the pair. MOVZ+MOVK
+  // should be clustered only when both write the same register. Architecturally
+  // a RAW dependency between MOVZ and MOVK would already imply WAW, because
+  // both have a single register operand.
+  assert(
+      (FirstMI == nullptr || mayHaveWAWDependency(*FirstMI, SecondMI, TRI)) &&
+      "read-after-write should have implied write-after-write for MOVZ+MOVK");
+  return true;
+}
+
 /// \brief Check if the instr pair, FirstMI and SecondMI, should be fused
 /// together. Given SecondMI, when FirstMI is unspecified, then check if
 /// SecondMI may be part of a fused pair at all.
@@ -695,6 +714,10 @@ static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
     ++NumFusedLiterals;
     return true;
   }
+  if (ST.hasFuseMovzMovk() && isMovzMovkPair(FirstMI, SecondMI, TRI, ST)) {
+    ++NumFusedMovzMovk;
+    return true;
+  }
   if (ST.hasFuseAddress() && isAddressLdStPair(FirstMI, SecondMI)) {
     ++NumFusedAddress;
     return true;
diff --git a/llvm/lib/Target/AArch64/AArch64Processors.td b/llvm/lib/Target/AArch64/AArch64Processors.td
index 68efd16459ada..9eb058922e317 100644
--- a/llvm/lib/Target/AArch64/AArch64Processors.td
+++ b/llvm/lib/Target/AArch64/AArch64Processors.td
@@ -427,7 +427,7 @@ def TuneAppleA14 : SubtargetFeature<"apple-a14", "ARMProcFamily", "AppleA14",
                                     FeatureFuseAddress,
                                     FeatureFuseArithmeticLogic,
                                     FeatureFuseCmpCSel,
-                                    FeatureFuseLiterals,
+                                    FeatureFuseMovzMovk,
                                     FeatureMaxInterleaveFactor4])>;
 
 def TuneAppleA15 : SubtargetFeature<"apple-a15", "ARMProcFamily", "AppleA15",
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
index 4fef12ab37ad8..c55624b93b3ca 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.cpp
@@ -698,3 +698,37 @@ bool AArch64Subtarget::fusesMOVImmPair(const MachineInstr *FirstMI,
                              FirstMI ? getMOVKShiftImm(*FirstMI) : 0,
                              SecondMI.getOpcode(), getMOVKShiftImm(SecondMI));
 }
+
+/// \p HasFirst is false when the 1st instruction is a wildcard.
+static bool fusesMovzMovkPairImpl(const AArch64Subtarget &ST, bool HasFirst,
+                                  unsigned FirstOpc, unsigned SecondOpc) {
+  assert(ST.hasFuseMovzMovk() && "the subtarget doesn't fuse MOVZ+MOVK");
+
+  switch (SecondOpc) {
+  case AArch64::MOVKWi:
+  case AArch64::MOVKXi:
+    // Assume the 1st instr to be a wildcard if it is unspecified.
+    if (!HasFirst)
+      return true;
+
+    switch (FirstOpc) {
+    case AArch64::MOVZWi:
+    case AArch64::MOVZXi:
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool AArch64Subtarget::fusesMovzMovkPair(unsigned FirstOpc,
+                                         unsigned SecondOpc) const {
+  return fusesMovzMovkPairImpl(*this, /*HasFirst=*/true, FirstOpc, SecondOpc);
+}
+
+bool AArch64Subtarget::fusesMovzMovkPair(const MachineInstr *FirstMI,
+                                         const MachineInstr &SecondMI) const {
+  return fusesMovzMovkPairImpl(*this, FirstMI != nullptr,
+                               FirstMI ? FirstMI->getOpcode() : 0,
+                               SecondMI.getOpcode());
+}
diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h
index 98cc97ae0a695..8ab2da51b6903 100644
--- a/llvm/lib/Target/AArch64/AArch64Subtarget.h
+++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h
@@ -265,7 +265,8 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
     return hasArithmeticBccFusion() || hasArithmeticCbzFusion() ||
            hasFuseAES() || hasFuseArithmeticLogic() || hasFuseCmpCSel() ||
            hasFuseFCmpFCSel() || hasFuseCmpCSet() || hasFuseAdrpAdd() ||
-           hasFuseLiterals() || hasFuseAppleSMECompute() || hasFuseFMinFMax();
+           hasFuseLiterals() || hasFuseMovzMovk() || hasFuseAppleSMECompute() ||
+           hasFuseFMinFMax();
   }
 
   /// Return true if the subtarget fuses this pair of move immediate
@@ -279,6 +280,22 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
   bool fusesMOVImmPair(const MachineInstr *FirstMI,
                        const MachineInstr &SecondMI) const;
 
+  /// Return true if the subtarget fuses this opcode pair as MOVZ+MOVK
+  /// instructions.
+  ///
+  /// This checks the opcode shape only. The caller is
+  /// responsible for establishing that the pair writes the same register.
+  bool fusesMovzMovkPair(unsigned FirstOpc, unsigned SecondOpc) const;
+
+  /// Return true if the subtarget fuses this pair as MOVZ+MOVK
+  /// instructions. The 1st instruction is a wildcard when it is nullptr, which
+  /// tells whether the 2nd one can be fused at all.
+  ///
+  /// This checks the opcode shape only. The caller is
+  /// responsible for establishing that the pair writes the same register.
+  bool fusesMovzMovkPair(const MachineInstr *FirstMI,
+                         const MachineInstr &SecondMI) const;
+
   unsigned getEpilogueVectorizationMinVF() const {
     return EpilogueVectorizationMinVF;
   }
diff --git a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-movz-movk.ll b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-movz-movk.ll
new file mode 100644
index 0000000000000..e4c5389a1467f
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-fuse-movz-movk.ll
@@ -0,0 +1,556 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-movz-movk | FileCheck %s --check-prefixes=CHECK,FUSE
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=-fuse-movz-movk | FileCheck %s --check-prefixes=CHECK,NOFUSE
+
+define double @movz() {
+; CHECK-LABEL: movz:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000000001234
+}
+
+define double @movz16() {
+; CHECK-LABEL: movz16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000012340000
+}
+
+define double @movz32() {
+; CHECK-LABEL: movz32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #20014547599360 // =0x123400000000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000123400000000
+}
+
+define double @movz48() {
+; CHECK-LABEL: movz48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1311673391471656960 // =0x1234000000000000
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234000000000000
+}
+
+define double @movn() {
+; CHECK-LABEL: movn:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFFFFFF1234
+}
+
+define double @movn16() {
+; CHECK-LABEL: movn16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFF1234FFFF
+}
+
+define double @movn32() {
+; CHECK-LABEL: movn32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-261456134144001 // =0xffff1234ffffffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF1234FFFFFFFF
+}
+
+define double @movn48() {
+; CHECK-LABEL: movn48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1311954866448367615 // =0x1234ffffffffffff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234FFFFFFFFFFFF
+}
+
+define double @orr() {
+; CHECK-LABEL: orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000100010001
+}
+
+define double @movz_movk16() {
+; CHECK-LABEL: movz_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000000056781234
+}
+
+define double @movz_movk32() {
+; CHECK-LABEL: movz_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000567800001234
+}
+
+define double @movz_movk48() {
+; CHECK-LABEL: movz_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #4660 // =0x1234
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678000000001234
+}
+
+define double @movz16_movk32() {
+; CHECK-LABEL: movz16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0000567812340000
+}
+
+define double @movz16_movk48() {
+; CHECK-LABEL: movz16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #305397760 // =0x12340000
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678000012340000
+}
+
+define double @movz32_movk48() {
+; CHECK-LABEL: movz32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #20014547599360 // =0x123400000000
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678123400000000
+}
+
+define double @movn_movk16() {
+; CHECK-LABEL: movn_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFFFFF56781234
+}
+
+define double @movn_movk32() {
+; CHECK-LABEL: movn_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF5678FFFF1234
+}
+
+define double @movn_movk48() {
+; CHECK-LABEL: movn_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-60876 // =0xffffffffffff1234
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678FFFFFFFF1234
+}
+
+define double @movn16_movk32() {
+; CHECK-LABEL: movn16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    movk x8, #22136, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFF56781234FFFF
+}
+
+define double @movn16_movk48() {
+; CHECK-LABEL: movn16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-3989504001 // =0xffffffff1234ffff
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x5678FFFF1234FFFF
+}
+
+define double @movn32_movk48() {
+; CHECK-LABEL: movn32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-261456134144001 // =0xffff1234ffffffff
+; CHECK-NEXT:    movk x8, #22136, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x56781234FFFFFFFF
+}
+
+define double @movn_eors() {
+; CHECK-LABEL: movn_eors:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-4661 // =0xffffffffffffedcb
+; CHECK-NEXT:    eor x8, x8, x8, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x00001234FFFFEDCB
+}
+
+define double @movn_eons() {
+; CHECK-LABEL: movn_eons:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #-4661 // =0xffffffffffffedcb
+; CHECK-NEXT:    eon x8, x8, x8, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0xFFFFEDCB00001234
+}
+
+define double @orr_eor() {
+; CHECK-LABEL: orr_eor:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #562945658585087 // =0x1ffff0001ffff
+; CHECK-NEXT:    eor x8, x8, #0x3fffe
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001FFFF00020001
+}
+
+define double @orr_movk0() {
+; CHECK-LABEL: orr_movk0:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000100011234
+}
+
+define double @orr_movk16() {
+; CHECK-LABEL: orr_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #16
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001000112340001
+}
+
+define double @orr_movk32() {
+; CHECK-LABEL: orr_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #32
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0001123400010001
+}
+
+define double @orr_movk48() {
+; CHECK-LABEL: orr_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #281479271743489 // =0x1000100010001
+; CHECK-NEXT:    movk x8, #4660, lsl #48
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x1234000100010001
+}
+
+define double @orr_orr() {
+; CHECK-LABEL: orr_orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #1125917086973956 // =0x4000400040004
+; CHECK-NEXT:    orr x8, x8, #0x1000100010001
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0005000500050005
+}
+
+define double @orr_and() {
+; CHECK-LABEL: orr_and:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov x8, #6148914691236517205 // =0x5555555555555555
+; CHECK-NEXT:    and x8, x8, #0xff00ff00ff00ff
+; CHECK-NEXT:    fmov d0, x8
+; CHECK-NEXT:    ret
+  ret double 0x0055005500550055
+}
+
+define double @movz_movk16_movk32() {
+; FUSE-LABEL: movz_movk16_movk32:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #16
+; FUSE-NEXT:    movk x8, #39612, lsl #32
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_movk32:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI30_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI30_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x00009ABC56781234
+}
+
+define double @movz_movk16_movk48() {
+; FUSE-LABEL: movz_movk16_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #16
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI31_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI31_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC000056781234
+}
+
+define double @movz_movk16_orrs() {
+; FUSE-LABEL: movz_movk16_orrs:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #4660, lsl #16
+; FUSE-NEXT:    orr x8, x8, x8, lsl #32
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk16_orrs:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI32_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI32_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x1234123412341234
+}
+
+define double @movz_movk32_movk48() {
+; FUSE-LABEL: movz_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #4660 // =0x1234
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI33_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI33_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC567800001234
+}
+
+define double @movz16_movk32_movk48() {
+; FUSE-LABEL: movz16_movk32_movk48:
+; FUSE:       // %bb.0:
+; FUSE-NEXT:    mov x8, #305397760 // =0x12340000
+; FUSE-NEXT:    movk x8, #22136, lsl #32
+; FUSE-NEXT:    movk x8, #39612, lsl #48
+; FUSE-NEXT:    fmov d0, x8
+; FUSE-NEXT:    ret
+;
+; NOFUSE-LABEL: movz16_movk32_movk48:
+; NOFUSE:       // %bb.0:
+; NOFUSE-NEXT:    adrp x8, .LCPI34_0
+; NOFUSE-NEXT:    ldr d0, [x8, :lo12:.LCPI34_0]
+; NOFUSE-NEXT:    ret
+  ret double 0x9ABC567812340000
+}
+
+define double @movn_movk16_eors() {
+; CHECK-LABEL: movn_movk16_eors:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI35_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI35_0]
+; CHECK-NEXT:    ret
+  ret double 0xA987EDCB56781234
+}
+
+define double @movn_movk16_movk32() {
+; CHECK-LABEL: movn_movk16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI36_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI36_0]
+; CHECK-NEXT:    ret
+  ret double 0xFFFF9ABC56781234
+}
+
+define double @movn_movk16_movk48() {
+; CHECK-LABEL: movn_movk16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI37_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI37_0]
+; CHECK-NEXT:    ret
+  ret double 0x9ABCFFFF56781234
+}
+
+define double @movn_movk32_movk48() {
+; CHECK-LABEL: movn_movk32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI38_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI38_0]
+; CHECK-NEXT:    ret
+  ret double 0x9ABC5678FFFF1234
+}
+
+define double @movn16_movk32_movk48() {
+; CHECK-LABEL: movn16_movk32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI39_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI39_0]
+; CHECK-NEXT:    ret
+  ret double 0x9ABC56781234FFFF
+}
+
+define double @orr_movk0_movk16() {
+; CHECK-LABEL: orr_movk0_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI40_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI40_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001000156781234
+}
+
+define double @orr_movk0_movk32() {
+; CHECK-LABEL: orr_movk0_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI41_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI41_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001567800011234
+}
+
+define double @orr_movk0_movk48() {
+; CHECK-LABEL: orr_movk0_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI42_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI42_0]
+; CHECK-NEXT:    ret
+  ret double 0x5678000100011234
+}
+
+define double @orr_movk16_movk32() {
+; CHECK-LABEL: orr_movk16_movk32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI43_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI43_0]
+; CHECK-NEXT:    ret
+  ret double 0x0001567812340001
+}
+
+define double @orr_movk16_movk48() {
+; CHECK-LABEL: orr_movk16_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI44_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI44_0]
+; CHECK-NEXT:    ret
+  ret double 0x5678000112340001
+}
+
+define double @orr_movk32_movk48() {
+; CHECK-LABEL: orr_movk32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI45_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI45_0]
+; CHECK-NEXT:    ret
+  ret double 0x5678123400010001
+}
+
+define double @movn_movk16_eons() {
+; CHECK-LABEL: movn_movk16_eons:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI46_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI46_0]
+; CHECK-NEXT:    ret
+  ret double 0xFFF567818AC7EDCB
+}
+
+define double @movz_movk16_movk32_movk48() {
+; CHECK-LABEL: movz_movk16_movk32_movk48:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    adrp x8, .LCPI47_0
+; CHECK-NEXT:    ldr d0, [x8, :lo12:.LCPI47_0]
+; CHECK-NEXT:    ret
+  ret double 0xABCD9ABC56781234
+}
+
+;; 32 bit FP immediate expansion shouldn't be affected by the limit,
+;; because we can always materialize it using up to 2 instructions - as
+;; `AArch64ExpandImm.cpp` asserts, which is already the default limit
+;; for non-optsize functions.
+
+define float @f32_movz() {
+; CHECK-LABEL: f32_movz:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 4660 to float)
+}
+
+define float @f32_movz16() {
+; CHECK-LABEL: f32_movz16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #305397760 // =0x12340000
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 305397760 to float)
+}
+
+define float @f32_movn() {
+; CHECK-LABEL: f32_movn:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #-60876 // =0xffff1234
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 -60876 to float)
+}
+
+define float @f32_movn16() {
+; CHECK-LABEL: f32_movn16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #305463295 // =0x1234ffff
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 305463295 to float)
+}
+
+define float @f32_orr() {
+; CHECK-LABEL: f32_orr:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #16843009 // =0x1010101
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 16843009 to float)
+}
+
+define float @f32_movz_movk16() {
+; CHECK-LABEL: f32_movz_movk16:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4660 // =0x1234
+; CHECK-NEXT:    movk w8, #3855, lsl #16
+; CHECK-NEXT:    fmov s0, w8
+; CHECK-NEXT:    ret
+  ret float bitcast (i32 252645940 to float)
+}
diff --git a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll
index 61604b5a13e27..07668ffadc368 100644
--- a/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll
+++ b/llvm/test/CodeGen/AArch64/fpimm-legal-expand-optsize.ll
@@ -1,6 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-literals | FileCheck %s
 ; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=-fuse-literals | FileCheck %s
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-movz-movk | FileCheck %s
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=-fuse-movz-movk | FileCheck %s
 
 ;; Representative tests for each class of FP immediate expansion that shouldn't apply
 ;; when optimizing for size.
diff --git a/llvm/test/CodeGen/AArch64/literal_pools_float_apple.ll b/llvm/test/CodeGen/AArch64/literal_pools_float_apple.ll
index 138d885679fb8..68a2f43e8e51a 100644
--- a/llvm/test/CodeGen/AArch64/literal_pools_float_apple.ll
+++ b/llvm/test/CodeGen/AArch64/literal_pools_float_apple.ll
@@ -118,11 +118,11 @@ define double @double_4mov() {
 ;
 ; APPLE-LABEL: double_4mov:
 ; APPLE:       ; %bb.0:
-; APPLE-NEXT:    mov x8, #4096 ; =0x1000
-; APPLE-NEXT:    movk x8, #8192, lsl #16
-; APPLE-NEXT:    movk x8, #12288, lsl #32
-; APPLE-NEXT:    movk x8, #16384, lsl #48
-; APPLE-NEXT:    fmov d0, x8
+; APPLE-NEXT:  Lloh0:
+; APPLE-NEXT:    adrp x8, lCPI7_0 at PAGE
+; APPLE-NEXT:  Lloh1:
+; APPLE-NEXT:    ldr d0, [x8, lCPI7_0 at PAGEOFF]
 ; APPLE-NEXT:    ret
+; APPLE-NEXT:    .loh AdrpLdr Lloh0, Lloh1
   ret double 0x4000300020001000
 }
diff --git a/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-post-ra.mir b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-post-ra.mir
new file mode 100644
index 0000000000000..a4be1c0682a06
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-post-ra.mir
@@ -0,0 +1,1124 @@
+# REQUIRES: asserts
+
+# RUN: llc -filetype=null %s -mtriple=aarch64-linux-gnu -mattr=+fuse-movz-movk -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-a14 -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-m5 -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+
+# RUN: llc -filetype=null %s -mtriple=aarch64-linux-gnu -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,NOFUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-m5 -mattr=-fuse-movz-movk -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,NOFUSE
+
+---
+name: movzw0_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkw0_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzw0_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzw0_movkw0_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzw0_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkw16_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzw0_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzw0_movkw16_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzw0_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx0_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzw0_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw0_movkx0_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzw0_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx16_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzw0_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw0_movkx16_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzw0_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx32_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzw0_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw0_movkx32_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzw0_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx48_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $w0 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzw0_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw0_movkx48_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $w1 = MOVZWi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzw16_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkw0_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzw16_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzw16_movkw0_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzw16_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkw16_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzw16_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzw16_movkw16_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzw16_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx0_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzw16_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw16_movkx0_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzw16_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx16_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzw16_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw16_movkx16_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzw16_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx32_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzw16_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw16_movkx32_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzw16_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx48_waw
+    ; CHECK: SU(0): $w0 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $w0 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzw16_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzw16_movkx48_nowaw
+    ; CHECK: SU(0): $w1 = MOVZWi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $w1 = MOVZWi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx0_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkw0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx0_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx0_movkw0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx0_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkw16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx0_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx0_movkw16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx0_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx0_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx0_movkx0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx0_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx0_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx0_movkx16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx0_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx32_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx0_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx0_movkx32_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx0_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx48_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x0 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx0_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx0_movkx48_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 0
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x1 = MOVZXi 1, 0
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx16_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkw0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx16_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx16_movkw0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx16_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkw16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx16_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx16_movkw16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx16_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx16_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx16_movkx0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx16_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx16_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx16_movkx16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx16_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx32_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx16_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx16_movkx32_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx16_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx48_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x0 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx16_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx16_movkx48_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 16
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x1 = MOVZXi 1, 16
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx32_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkw0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx32_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx32_movkw0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx32_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkw16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx32_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx32_movkw16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx32_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx32_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx32_movkx0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx32_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx32_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx32_movkx16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx32_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx32_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx32_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx32_movkx32_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx32_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx48_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x0 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx32_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx32_movkx48_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 32
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x1 = MOVZXi 1, 32
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx48_movkw0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkw0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx48_movkw0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx48_movkw0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 0
+...
+
+---
+name: movzx48_movkw16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkw16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx48_movkw16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0
+    ; CHECK-LABEL: movzx48_movkw16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $w0 = MOVKWi $w0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $w0 = MOVKWi $w0, 2, 16
+...
+
+---
+name: movzx48_movkx0_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx0_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx48_movkx0_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx48_movkx0_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 0
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 0
+...
+
+---
+name: movzx48_movkx16_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx16_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx48_movkx16_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx48_movkx16_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 16
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 16
+...
+
+---
+name: movzx48_movkx32_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx32_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx48_movkx32_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx48_movkx32_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 32
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 32
+...
+
+---
+name: movzx48_movkx48_waw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx48_waw
+    ; CHECK: SU(0): $x0 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x0 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
+
+---
+name: movzx48_movkx48_nowaw
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0
+    ; CHECK-LABEL: movzx48_movkx48_nowaw
+    ; CHECK: SU(0): $x1 = MOVZXi 1, 48
+    ; CHECK-NOT: Cluster
+    ; CHECK: SU(2): $x0 = MOVKXi $x0(tied-def 0), 2, 48
+    $x1 = MOVZXi 1, 48
+    $w9 = MOVZWi 9, 0
+    $x0 = MOVKXi $x0, 2, 48
+...
diff --git a/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-pre-ra.mir b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-pre-ra.mir
new file mode 100644
index 0000000000000..d80aa4980ce4d
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk-pre-ra.mir
@@ -0,0 +1,584 @@
+# REQUIRES: asserts
+
+# RUN: llc -filetype=null %s -mtriple=aarch64-unknown -mattr=+fuse-movz-movk -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-a14 -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-m5 -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,FUSE
+
+# RUN: llc -filetype=null %s -mtriple=aarch64-unknown -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,NOFUSE
+# RUN: llc -filetype=null %s -mtriple=arm64-apple-macosx -mcpu=apple-m5 -mattr=-fuse-movz-movk -passes=machine-scheduler -misched-print-dags 2>&1 | FileCheck %s --check-prefixes=CHECK,NOFUSE
+
+---
+name: movzw0_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkw0
+    ; CHECK: SU(0): %0:gpr32 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr32 = MOVKWi %0:gpr32(tied-def 0), 2, 0
+    %0:gpr32 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr32 = MOVKWi %0, 2, 0
+...
+
+---
+name: movzw0_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkw16
+    ; CHECK: SU(0): %0:gpr32 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr32 = MOVKWi %0:gpr32(tied-def 0), 2, 16
+    %0:gpr32 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr32 = MOVKWi %0, 2, 16
+...
+
+---
+name: movzw0_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx0
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzw0_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx16
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzw0_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx32
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzw0_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw0_movkx48
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    undef %0.sub_32:gpr64 = MOVZWi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
+
+---
+name: movzw16_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkw0
+    ; CHECK: SU(0): %0:gpr32 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr32 = MOVKWi %0:gpr32(tied-def 0), 2, 0
+    %0:gpr32 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr32 = MOVKWi %0, 2, 0
+...
+
+---
+name: movzw16_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkw16
+    ; CHECK: SU(0): %0:gpr32 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr32 = MOVKWi %0:gpr32(tied-def 0), 2, 16
+    %0:gpr32 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr32 = MOVKWi %0, 2, 16
+...
+
+---
+name: movzw16_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx0
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzw16_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx16
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzw16_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx32
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzw16_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzw16_movkx48
+    ; CHECK: SU(0): undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    undef %0.sub_32:gpr64 = MOVZWi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
+
+---
+name: movzx0_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkw0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 0
+...
+
+---
+name: movzx0_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkw16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 16
+...
+
+---
+name: movzx0_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzx0_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzx0_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx32
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzx0_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx0_movkx48
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 0
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    %0:gpr64 = MOVZXi 1, 0
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
+
+---
+name: movzx16_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkw0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 0
+...
+
+---
+name: movzx16_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkw16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 16
+...
+
+---
+name: movzx16_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzx16_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzx16_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx32
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzx16_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx16_movkx48
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 16
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    %0:gpr64 = MOVZXi 1, 16
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
+
+---
+name: movzx32_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkw0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 0
+...
+
+---
+name: movzx32_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkw16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 16
+...
+
+---
+name: movzx32_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzx32_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzx32_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx32
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzx32_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx32_movkx48
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 32
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    %0:gpr64 = MOVZXi 1, 32
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
+
+---
+name: movzx48_movkw0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkw0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 0
+...
+
+---
+name: movzx48_movkw16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkw16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0.sub_32:gpr64 = MOVKWi %0.sub_32:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0.sub_32:gpr64 = MOVKWi %0.sub_32, 2, 16
+...
+
+---
+name: movzx48_movkx0
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx0
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 0
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 0
+...
+
+---
+name: movzx48_movkx16
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx16
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 16
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 16
+...
+
+---
+name: movzx48_movkx32
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx32
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 32
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 32
+...
+
+---
+name: movzx48_movkx48
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: movzx48_movkx48
+    ; CHECK: SU(0): %0:gpr64 = MOVZXi 1, 48
+    ; CHECK: Successors:
+    ; FUSE: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; NOFUSE-NOT: SU(2): Ord  Latency={{[0-9]+}} Cluster
+    ; CHECK: SU(2): dead %0:gpr64 = MOVKXi %0:gpr64(tied-def 0), 2, 48
+    %0:gpr64 = MOVZXi 1, 48
+    %1:gpr32 = MOVZWi 9, 0
+    %0:gpr64 = MOVKXi %0, 2, 48
+...
diff --git a/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk.ll b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk.ll
new file mode 100644
index 0000000000000..3428faa13db9d
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/misched-fusion-movz-movk.ll
@@ -0,0 +1,60 @@
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mattr=+fuse-movz-movk,+use-postra-scheduler | FileCheck %s --check-prefixes=CHECK
+
+;; Lack post-RA scheduling, but should behave the same by plain pseudo-expansion
+; RUN: llc %s -o - -mtriple=arm64-apple-macosx -mcpu=apple-a13 -mattr=+fuse-movz-movk | FileCheck %s --check-prefixes=CHECK
+; RUN: llc %s -o - -mtriple=arm64-apple-macosx -mcpu=apple-a14 | FileCheck %s --check-prefixes=CHECK
+; RUN: llc %s -o - -mtriple=arm64-apple-macosx -mcpu=apple-m5 | FileCheck %s --check-prefixes=CHECK
+
+; CHECK-LABEL: movz_movk16_w:
+; CHECK:      mov  [[R:w[0-9]+]], #48879
+; CHECK-NEXT: movk [[R]], #61536, lsl #16
+define i32 @movz_movk16_w(i32 %a, i32 %b) {
+  %c = add i32 %a, -262095121
+  %r = add i32 %c, %b
+  ret i32 %r
+}
+
+; CHECK-LABEL: movz_movk32_x:
+; CHECK:      mov  [[R:x[0-9]+]], #4660
+; CHECK-NEXT: movk [[R]], #22136, lsl #32
+define i64 @movz_movk32_x(i64 %a, i64 %b) {
+  %c = add i64 %a, 95073396068916
+  %r = add i64 %c, %b
+  ret i64 %r
+}
+
+; CHECK-LABEL: movz_movk48_x:
+; CHECK:      mov  [[R:x[0-9]+]], #4660
+; CHECK-NEXT: movk [[R]], #22136, lsl #48
+define i64 @movz_movk48_x(i64 %a, i64 %b) {
+  %c = add i64 %a, 6230730084467085876
+  %r = add i64 %c, %b
+  ret i64 %r
+}
+
+; CHECK-LABEL: movz16_movk32_x:
+; CHECK:      mov  [[R:x[0-9]+]], #305397760
+; CHECK-NEXT: movk [[R]], #22136, lsl #32
+define i64 @movz16_movk32_x(i64 %a, i64 %b) {
+  %c = add i64 %a, 95073701462016
+  %r = add i64 %c, %b
+  ret i64 %r
+}
+
+; CHECK-LABEL: movz16_movk48_x:
+; CHECK:      mov  [[R:x[0-9]+]], #305397760
+; CHECK-NEXT: movk [[R]], #22136, lsl #48
+define i64 @movz16_movk48_x(i64 %a, i64 %b) {
+  %c = add i64 %a, 6230730084772478976
+  %r = add i64 %c, %b
+  ret i64 %r
+}
+
+; CHECK-LABEL: movz32_movk48_x:
+; CHECK:      mov  [[R:x[0-9]+]], #20014547599360
+; CHECK-NEXT: movk [[R]], #22136, lsl #48
+define i64 @movz32_movk48_x(i64 %a, i64 %b) {
+  %c = add i64 %a, 6230750099014680576
+  %r = add i64 %c, %b
+  ret i64 %r
+}
diff --git a/llvm/test/TableGen/aarch64-apple-tuning-features.td b/llvm/test/TableGen/aarch64-apple-tuning-features.td
index 43ef5df90af39..dbea30e3cb4e3 100644
--- a/llvm/test/TableGen/aarch64-apple-tuning-features.td
+++ b/llvm/test/TableGen/aarch64-apple-tuning-features.td
@@ -108,7 +108,7 @@
 // CHECK-NEXT:    FeatureFuseArithmeticLogic,
 // CHECK-NEXT:    FeatureFuseCryptoEOR,
 // CHECK-NEXT:    FeatureFuseCmpCSel,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureStorePairSuppress,
@@ -137,7 +137,7 @@
 // CHECK-NEXT:    FeatureFuseArithmeticLogic,
 // CHECK-NEXT:    FeatureFuseCryptoEOR,
 // CHECK-NEXT:    FeatureFuseCmpCSel,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureStorePairSuppress,
@@ -167,7 +167,7 @@
 // CHECK-NEXT:    FeatureFuseCryptoEOR,
 // CHECK-NEXT:    FeatureFuseCmpCSel,
 // CHECK-NEXT:    FeatureFuseFCmpFCSel,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureStorePairSuppress,
@@ -197,7 +197,7 @@
 // CHECK-NEXT:    FeatureFuseCryptoEOR,
 // CHECK-NEXT:    FeatureFuseCmpCSel,
 // CHECK-NEXT:    FeatureFuseFCmpFCSel,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureStorePairSuppress,
@@ -250,7 +250,7 @@
 // CHECK-NEXT:    FeatureFuseCryptoEOR,
 // CHECK-NEXT:    FeatureFuseCmpCSel,
 // CHECK-NEXT:    FeatureFuseFCmpFCSel,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureZCRegMoveFPR128,
@@ -281,7 +281,7 @@
 // CHECK-NEXT:    FeatureFuseCmpCSel,
 // CHECK-NEXT:    FeatureFuseFCmpFCSel,
 // CHECK-NEXT:    FeatureFuseFMinFMax,
-// CHECK-NEXT:    FeatureFuseLiterals,
+// CHECK-NEXT:    FeatureFuseMovzMovk,
 // CHECK-NEXT:    FeatureMaxInterleaveFactor4,
 // CHECK-NEXT:    FeatureNoZCZeroingFPR64,
 // CHECK-NEXT:    FeatureZCRegMoveFPR128,



More information about the llvm-commits mailing list