[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