[llvm] [AArch64] Restrict FP imm ISel by accurate subtarget macro-fusion (PR #214849)

Tomer Shafir via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 8 01:45:25 PDT 2026


https://github.com/tomershafir updated https://github.com/llvm/llvm-project/pull/214849

>From 53305993c63a01adcbc9696ea5fefdf050b849e6 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] [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..14e621bb96330
--- /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 }



More information about the llvm-commits mailing list