Skip to content

[TTI][TLI] Support scalable immediates with isLegalAddImmediate #84173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2024

Conversation

huntergr-arm
Copy link
Collaborator

Adds a second parameter (default to 0) to isLegalAddImmediate, to represent a scalable immediate.

Extends the AArch64 implementation to match immediates based on vscale * base_register_size * immediate in the range [-32,31].

See the vscale-aware LSR RFC for reference: https://discourse.llvm.org/t/rfc-vscale-aware-loopstrengthreduce/77131

@llvmbot
Copy link
Member

llvmbot commented Mar 6, 2024

@llvm/pr-subscribers-backend-loongarch
@llvm/pr-subscribers-backend-powerpc
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-backend-aarch64

@llvm/pr-subscribers-backend-systemz

Author: Graham Hunter (huntergr-arm)

Changes

Adds a second parameter (default to 0) to isLegalAddImmediate, to represent a scalable immediate.

Extends the AArch64 implementation to match immediates based on vscale * base_register_size * immediate in the range [-32,31].

See the vscale-aware LSR RFC for reference: https://discourse.llvm.org/t/rfc-vscale-aware-loopstrengthreduce/77131


Full diff: https://github.com/llvm/llvm-project/pull/84173.diff

21 Files Affected:

  • (modified) llvm/include/llvm/Analysis/TargetTransformInfo.h (+4-4)
  • (modified) llvm/include/llvm/Analysis/TargetTransformInfoImpl.h (+3-1)
  • (modified) llvm/include/llvm/CodeGen/BasicTTIImpl.h (+2-2)
  • (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+2-2)
  • (modified) llvm/lib/Analysis/TargetTransformInfo.cpp (+3-2)
  • (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (+19-1)
  • (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/ARM/ARMISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMISelLowering.h (+2-1)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.h (+2-1)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+3-2)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/SystemZ/SystemZISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/SystemZ/SystemZISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/X86/X86ISelLowering.h (+2-1)
  • (modified) llvm/unittests/Target/AArch64/CMakeLists.txt (+1)
  • (added) llvm/unittests/Target/AArch64/Immediates.cpp (+75)
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 4eab357f1b33b6..f9dd7bbc3dcec8 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -694,7 +694,7 @@ class TargetTransformInfo {
   /// Return true if the specified immediate is legal add immediate, that
   /// is the target has add instructions which can add a register with the
   /// immediate without having to materialize the immediate into a register.
-  bool isLegalAddImmediate(int64_t Imm) const;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const;
 
   /// Return true if the specified immediate is legal icmp immediate,
   /// that is the target has icmp instructions which can compare a register
@@ -1834,7 +1834,7 @@ class TargetTransformInfo::Concept {
       APInt &UndefElts, APInt &UndefElts2, APInt &UndefElts3,
       std::function<void(Instruction *, unsigned, APInt, APInt &)>
           SimplifyAndSetOp) = 0;
-  virtual bool isLegalAddImmediate(int64_t Imm) = 0;
+  virtual bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) = 0;
   virtual bool isLegalICmpImmediate(int64_t Imm) = 0;
   virtual bool isLegalAddressingMode(Type *Ty, GlobalValue *BaseGV,
                                      int64_t BaseOffset, bool HasBaseReg,
@@ -2292,8 +2292,8 @@ class TargetTransformInfo::Model final : public TargetTransformInfo::Concept {
         IC, II, DemandedElts, UndefElts, UndefElts2, UndefElts3,
         SimplifyAndSetOp);
   }
-  bool isLegalAddImmediate(int64_t Imm) override {
-    return Impl.isLegalAddImmediate(Imm);
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) override {
+    return Impl.isLegalAddImmediate(Imm, ScalableImm);
   }
   bool isLegalICmpImmediate(int64_t Imm) override {
     return Impl.isLegalICmpImmediate(Imm);
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 95fb13d1c97154..7fbdfe3fc68437 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -214,7 +214,9 @@ class TargetTransformInfoImplBase {
   void getPeelingPreferences(Loop *, ScalarEvolution &,
                              TTI::PeelingPreferences &) const {}
 
-  bool isLegalAddImmediate(int64_t Imm) const { return false; }
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) const {
+    return false;
+  }
 
   bool isLegalICmpImmediate(int64_t Imm) const { return false; }
 
diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
index 61f6564e8cd79b..ac8cd28689d35a 100644
--- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h
+++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
@@ -324,8 +324,8 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
     return nullptr;
   }
 
-  bool isLegalAddImmediate(int64_t imm) {
-    return getTLI()->isLegalAddImmediate(imm);
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) {
+    return getTLI()->isLegalAddImmediate(Imm, ScalableImm);
   }
 
   bool isLegalICmpImmediate(int64_t imm) {
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 2f164a460db843..57844dd82d6410 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -2766,8 +2766,8 @@ class TargetLoweringBase {
   /// Return true if the specified immediate is legal add immediate, that is the
   /// target has add instructions which can add a register with the immediate
   /// without having to materialize the immediate into a register.
-  virtual bool isLegalAddImmediate(int64_t) const {
-    return true;
+  virtual bool isLegalAddImmediate(int64_t, int64_t ScalableImm = 0) const {
+    return !ScalableImm;
   }
 
   /// Return true if the specified immediate is legal for the value input of a
diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 15311be4dba277..8791ae4c3744e5 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -391,8 +391,9 @@ void TargetTransformInfo::getPeelingPreferences(Loop *L, ScalarEvolution &SE,
   return TTIImpl->getPeelingPreferences(L, SE, PP);
 }
 
-bool TargetTransformInfo::isLegalAddImmediate(int64_t Imm) const {
-  return TTIImpl->isLegalAddImmediate(Imm);
+bool TargetTransformInfo::isLegalAddImmediate(int64_t Imm,
+                                              int64_t ScalableImm) const {
+  return TTIImpl->isLegalAddImmediate(Imm, ScalableImm);
 }
 
 bool TargetTransformInfo::isLegalICmpImmediate(int64_t Imm) const {
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2290223a06f8ef..e53e8ce6a3d227 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -16559,7 +16559,25 @@ LLT AArch64TargetLowering::getOptimalMemOpLLT(
 }
 
 // 12-bit optionally shifted immediates are legal for adds.
-bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed) const {
+bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed,
+                                                int64_t ScalableImm) const {
+  if (ScalableImm) {
+    // Scalable immediates require SVE support; mixed fixed + scalable
+    // immediates are not supported by the current instructions.
+    if (Immed || !Subtarget->hasSVE())
+      return false;
+
+    // addvl's immediates are in terms of the number of bytes in a register.
+    // Since there are 16 in the base supported size (128bits), we need to
+    // divide the immediate by that much to give us a useful immediate to
+    // multiply by vscale. We can't have a remainder as a result of this.
+    if (ScalableImm % 16 != 0)
+      return false;
+    int64_t Imm = ScalableImm / 16;
+
+    return Imm >= -32 && Imm <= 31;
+  }
+
   if (Immed == std::numeric_limits<int64_t>::min()) {
     LLVM_DEBUG(dbgs() << "Illegal add imm " << Immed
                       << ": avoid UB for INT64_MIN\n");
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 68341c199e0a2a..adec93d7e6aea1 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -688,7 +688,7 @@ class AArch64TargetLowering : public TargetLowering {
   bool lowerInterleaveIntrinsicToStore(IntrinsicInst *II,
                                        StoreInst *SI) const override;
 
-  bool isLegalAddImmediate(int64_t) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isLegalICmpImmediate(int64_t) const override;
 
   bool isMulAddWithConstProfitable(SDValue AddNode,
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index dc81178311b6d8..65a15f959ff4d9 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -19722,7 +19722,8 @@ bool ARMTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
 /// *or sub* immediate, that is the target has add or sub instructions which can
 /// add a register with the immediate without having to materialize the
 /// immediate into a register.
-bool ARMTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool ARMTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   // Same encoding for add/sub, just flip the sign.
   int64_t AbsImm = std::abs(Imm);
   if (!Subtarget->isThumb())
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h
index b13ddf697cb806..70bbb15a1772f8 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.h
+++ b/llvm/lib/Target/ARM/ARMISelLowering.h
@@ -494,7 +494,8 @@ class VectorType;
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     /// getPreIndexedAddressParts - returns true by value, base pointer and
     /// offset pointer and addressing mode by reference if the node's address
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index c8e955a23336d7..f975fc52b2f774 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -4933,7 +4933,8 @@ bool LoongArchTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<12>(Imm);
 }
 
-bool LoongArchTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool LoongArchTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                                  int64_t ScalableImm) const {
   return isInt<12>(Imm);
 }
 
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 9e9ac0b8269291..b59a4dfcd9ead3 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -222,7 +222,7 @@ class LoongArchTargetLowering : public TargetLowering {
                              Instruction *I = nullptr) const override;
 
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isZExtFree(SDValue Val, EVT VT2) const override;
   bool isSExtCheaperThanZExt(EVT SrcVT, EVT DstVT) const override;
 
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 68c80dd9aa5c76..2aaf7b24d52544 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -17300,7 +17300,8 @@ bool PPCTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<16>(Imm) || isUInt<16>(Imm);
 }
 
-bool PPCTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool PPCTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   return isInt<16>(Imm) || isUInt<16>(Imm);
 }
 
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 0bdfdcd15441f4..a1b404bd655b36 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -1023,7 +1023,8 @@ namespace llvm {
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     /// isTruncateFree - Return true if it's free to truncate a value of
     /// type Ty1 to type Ty2. e.g. On PPC it's free to truncate a i64 value in
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 4c3dc63afd878d..c6b02b8b26856b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -1783,8 +1783,9 @@ bool RISCVTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<12>(Imm);
 }
 
-bool RISCVTargetLowering::isLegalAddImmediate(int64_t Imm) const {
-  return isInt<12>(Imm);
+bool RISCVTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                              int64_t ScalableImm) const {
+  return !ScalableImm && isInt<12>(Imm);
 }
 
 // On RV32, 64-bit integers are split into their high and low parts and held
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index f90cb4df604761..61fb5809c9b8dd 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -475,7 +475,7 @@ class RISCVTargetLowering : public TargetLowering {
                              unsigned AS,
                              Instruction *I = nullptr) const override;
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isTruncateFree(Type *SrcTy, Type *DstTy) const override;
   bool isTruncateFree(EVT SrcVT, EVT DstVT) const override;
   bool isZExtFree(SDValue Val, EVT VT2) const override;
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 3b85a6ac0371ed..a2aa33ff059489 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -944,7 +944,8 @@ bool SystemZTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<32>(Imm) || isUInt<32>(Imm);
 }
 
-bool SystemZTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool SystemZTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                                int64_t ScalableImm) const {
   // We can use ALGFI or SLGFI.
   return isUInt<32>(Imm) || isUInt<32>(-Imm);
 }
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index baf4ba41654879..274b23b4d28935 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -477,7 +477,7 @@ class SystemZTargetLowering : public TargetLowering {
   AtomicExpansionKind
   shouldExpandAtomicRMWInIR(AtomicRMWInst *RMW) const override;
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty,
                              unsigned AS,
                              Instruction *I = nullptr) const override;
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6eaaec407dbb08..d5f5ef24e56d5d 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -33882,7 +33882,8 @@ bool X86TargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<32>(Imm);
 }
 
-bool X86TargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool X86TargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   // Can also use sub to handle negated immediates.
   return isInt<32>(Imm);
 }
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index fe1943b5760844..b3cccee0e67bf1 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -1329,7 +1329,8 @@ namespace llvm {
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     bool isLegalStoreImmediate(int64_t Imm) const override;
 
diff --git a/llvm/unittests/Target/AArch64/CMakeLists.txt b/llvm/unittests/Target/AArch64/CMakeLists.txt
index dacd919ba1e33b..64ab991ac479a4 100644
--- a/llvm/unittests/Target/AArch64/CMakeLists.txt
+++ b/llvm/unittests/Target/AArch64/CMakeLists.txt
@@ -29,6 +29,7 @@ add_llvm_target_unittest(AArch64Tests
   MatrixRegisterAliasing.cpp
   SMEAttributesTest.cpp
   AArch64SVESchedPseudoTest.cpp
+  Immediates.cpp
   )
 
 set_property(TARGET AArch64Tests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
diff --git a/llvm/unittests/Target/AArch64/Immediates.cpp b/llvm/unittests/Target/AArch64/Immediates.cpp
new file mode 100644
index 00000000000000..5f5895e44c6110
--- /dev/null
+++ b/llvm/unittests/Target/AArch64/Immediates.cpp
@@ -0,0 +1,75 @@
+#include "AArch64Subtarget.h"
+#include "AArch64TargetMachine.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "gtest/gtest.h"
+#include <initializer_list>
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+struct TestCase {
+  int64_t FixedImm;
+  int64_t ScalableImm;
+  bool Result;
+};
+
+const std::initializer_list<TestCase> Tests = {
+    // FixedImm, ScalableImm, Result
+    // No change, easily 'supported'
+    {0, 0, true},
+
+    // Simple fixed immediate cases
+    // +8
+    {8, 0, true},
+    // -16
+    {-16, 0, true},
+
+    // Scalable; addvl increments by whole registers, range [-32,31]
+    // +(16 * vscale), one register's worth
+    {0, 16, true},
+    // +(8 * vscale), half a register's worth
+    {0, 8, false},
+    // -(32 * 16 * vscale)
+    {0, -512, true},
+    // -(33 * 16 * vscale)
+    {0, -528, false},
+    // +(31 * 16 * vscale)
+    {0, 496, true},
+    // +(32 * 16 * vscale)
+    {0, 512, false},
+
+    // Mixed; not supported.
+    // +(16 + (16 * vscale)) -- one register's worth + 16
+    {16, 16, false},
+};
+} // namespace
+
+TEST(Immediates, Immediates) {
+  LLVMInitializeAArch64TargetInfo();
+  LLVMInitializeAArch64Target();
+  LLVMInitializeAArch64TargetMC();
+
+  std::string Error;
+  auto TT = Triple::normalize("aarch64");
+  const Target *T = TargetRegistry::lookupTarget(TT, Error);
+
+  std::unique_ptr<TargetMachine> TM(
+      T->createTargetMachine(TT, "generic", "+sve", TargetOptions(),
+                             std::nullopt, std::nullopt,
+                             CodeGenOptLevel::Default));
+  AArch64Subtarget ST(TM->getTargetTriple(), TM->getTargetCPU(),
+                      TM->getTargetCPU(), TM->getTargetFeatureString(), *TM,
+                      true);
+
+  auto *TLI = ST.getTargetLowering();
+
+  for (const auto &Test : Tests) {
+    ASSERT_EQ(TLI->isLegalAddImmediate(Test.FixedImm, Test.ScalableImm),
+              Test.Result);
+  }
+}

@llvmbot
Copy link
Member

llvmbot commented Mar 6, 2024

@llvm/pr-subscribers-backend-x86

Author: Graham Hunter (huntergr-arm)

Changes

Adds a second parameter (default to 0) to isLegalAddImmediate, to represent a scalable immediate.

Extends the AArch64 implementation to match immediates based on vscale * base_register_size * immediate in the range [-32,31].

See the vscale-aware LSR RFC for reference: https://discourse.llvm.org/t/rfc-vscale-aware-loopstrengthreduce/77131


Full diff: https://github.com/llvm/llvm-project/pull/84173.diff

21 Files Affected:

  • (modified) llvm/include/llvm/Analysis/TargetTransformInfo.h (+4-4)
  • (modified) llvm/include/llvm/Analysis/TargetTransformInfoImpl.h (+3-1)
  • (modified) llvm/include/llvm/CodeGen/BasicTTIImpl.h (+2-2)
  • (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+2-2)
  • (modified) llvm/lib/Analysis/TargetTransformInfo.cpp (+3-2)
  • (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (+19-1)
  • (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/ARM/ARMISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/ARM/ARMISelLowering.h (+2-1)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/PowerPC/PPCISelLowering.h (+2-1)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+3-2)
  • (modified) llvm/lib/Target/RISCV/RISCVISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/SystemZ/SystemZISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/SystemZ/SystemZISelLowering.h (+1-1)
  • (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/X86/X86ISelLowering.h (+2-1)
  • (modified) llvm/unittests/Target/AArch64/CMakeLists.txt (+1)
  • (added) llvm/unittests/Target/AArch64/Immediates.cpp (+75)
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 4eab357f1b33b6..f9dd7bbc3dcec8 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -694,7 +694,7 @@ class TargetTransformInfo {
   /// Return true if the specified immediate is legal add immediate, that
   /// is the target has add instructions which can add a register with the
   /// immediate without having to materialize the immediate into a register.
-  bool isLegalAddImmediate(int64_t Imm) const;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const;
 
   /// Return true if the specified immediate is legal icmp immediate,
   /// that is the target has icmp instructions which can compare a register
@@ -1834,7 +1834,7 @@ class TargetTransformInfo::Concept {
       APInt &UndefElts, APInt &UndefElts2, APInt &UndefElts3,
       std::function<void(Instruction *, unsigned, APInt, APInt &)>
           SimplifyAndSetOp) = 0;
-  virtual bool isLegalAddImmediate(int64_t Imm) = 0;
+  virtual bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) = 0;
   virtual bool isLegalICmpImmediate(int64_t Imm) = 0;
   virtual bool isLegalAddressingMode(Type *Ty, GlobalValue *BaseGV,
                                      int64_t BaseOffset, bool HasBaseReg,
@@ -2292,8 +2292,8 @@ class TargetTransformInfo::Model final : public TargetTransformInfo::Concept {
         IC, II, DemandedElts, UndefElts, UndefElts2, UndefElts3,
         SimplifyAndSetOp);
   }
-  bool isLegalAddImmediate(int64_t Imm) override {
-    return Impl.isLegalAddImmediate(Imm);
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) override {
+    return Impl.isLegalAddImmediate(Imm, ScalableImm);
   }
   bool isLegalICmpImmediate(int64_t Imm) override {
     return Impl.isLegalICmpImmediate(Imm);
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
index 95fb13d1c97154..7fbdfe3fc68437 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
@@ -214,7 +214,9 @@ class TargetTransformInfoImplBase {
   void getPeelingPreferences(Loop *, ScalarEvolution &,
                              TTI::PeelingPreferences &) const {}
 
-  bool isLegalAddImmediate(int64_t Imm) const { return false; }
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) const {
+    return false;
+  }
 
   bool isLegalICmpImmediate(int64_t Imm) const { return false; }
 
diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
index 61f6564e8cd79b..ac8cd28689d35a 100644
--- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h
+++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h
@@ -324,8 +324,8 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
     return nullptr;
   }
 
-  bool isLegalAddImmediate(int64_t imm) {
-    return getTLI()->isLegalAddImmediate(imm);
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm) {
+    return getTLI()->isLegalAddImmediate(Imm, ScalableImm);
   }
 
   bool isLegalICmpImmediate(int64_t imm) {
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 2f164a460db843..57844dd82d6410 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -2766,8 +2766,8 @@ class TargetLoweringBase {
   /// Return true if the specified immediate is legal add immediate, that is the
   /// target has add instructions which can add a register with the immediate
   /// without having to materialize the immediate into a register.
-  virtual bool isLegalAddImmediate(int64_t) const {
-    return true;
+  virtual bool isLegalAddImmediate(int64_t, int64_t ScalableImm = 0) const {
+    return !ScalableImm;
   }
 
   /// Return true if the specified immediate is legal for the value input of a
diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp
index 15311be4dba277..8791ae4c3744e5 100644
--- a/llvm/lib/Analysis/TargetTransformInfo.cpp
+++ b/llvm/lib/Analysis/TargetTransformInfo.cpp
@@ -391,8 +391,9 @@ void TargetTransformInfo::getPeelingPreferences(Loop *L, ScalarEvolution &SE,
   return TTIImpl->getPeelingPreferences(L, SE, PP);
 }
 
-bool TargetTransformInfo::isLegalAddImmediate(int64_t Imm) const {
-  return TTIImpl->isLegalAddImmediate(Imm);
+bool TargetTransformInfo::isLegalAddImmediate(int64_t Imm,
+                                              int64_t ScalableImm) const {
+  return TTIImpl->isLegalAddImmediate(Imm, ScalableImm);
 }
 
 bool TargetTransformInfo::isLegalICmpImmediate(int64_t Imm) const {
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2290223a06f8ef..e53e8ce6a3d227 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -16559,7 +16559,25 @@ LLT AArch64TargetLowering::getOptimalMemOpLLT(
 }
 
 // 12-bit optionally shifted immediates are legal for adds.
-bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed) const {
+bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed,
+                                                int64_t ScalableImm) const {
+  if (ScalableImm) {
+    // Scalable immediates require SVE support; mixed fixed + scalable
+    // immediates are not supported by the current instructions.
+    if (Immed || !Subtarget->hasSVE())
+      return false;
+
+    // addvl's immediates are in terms of the number of bytes in a register.
+    // Since there are 16 in the base supported size (128bits), we need to
+    // divide the immediate by that much to give us a useful immediate to
+    // multiply by vscale. We can't have a remainder as a result of this.
+    if (ScalableImm % 16 != 0)
+      return false;
+    int64_t Imm = ScalableImm / 16;
+
+    return Imm >= -32 && Imm <= 31;
+  }
+
   if (Immed == std::numeric_limits<int64_t>::min()) {
     LLVM_DEBUG(dbgs() << "Illegal add imm " << Immed
                       << ": avoid UB for INT64_MIN\n");
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 68341c199e0a2a..adec93d7e6aea1 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -688,7 +688,7 @@ class AArch64TargetLowering : public TargetLowering {
   bool lowerInterleaveIntrinsicToStore(IntrinsicInst *II,
                                        StoreInst *SI) const override;
 
-  bool isLegalAddImmediate(int64_t) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isLegalICmpImmediate(int64_t) const override;
 
   bool isMulAddWithConstProfitable(SDValue AddNode,
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index dc81178311b6d8..65a15f959ff4d9 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -19722,7 +19722,8 @@ bool ARMTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
 /// *or sub* immediate, that is the target has add or sub instructions which can
 /// add a register with the immediate without having to materialize the
 /// immediate into a register.
-bool ARMTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool ARMTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   // Same encoding for add/sub, just flip the sign.
   int64_t AbsImm = std::abs(Imm);
   if (!Subtarget->isThumb())
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.h b/llvm/lib/Target/ARM/ARMISelLowering.h
index b13ddf697cb806..70bbb15a1772f8 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.h
+++ b/llvm/lib/Target/ARM/ARMISelLowering.h
@@ -494,7 +494,8 @@ class VectorType;
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     /// getPreIndexedAddressParts - returns true by value, base pointer and
     /// offset pointer and addressing mode by reference if the node's address
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index c8e955a23336d7..f975fc52b2f774 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -4933,7 +4933,8 @@ bool LoongArchTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<12>(Imm);
 }
 
-bool LoongArchTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool LoongArchTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                                  int64_t ScalableImm) const {
   return isInt<12>(Imm);
 }
 
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 9e9ac0b8269291..b59a4dfcd9ead3 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -222,7 +222,7 @@ class LoongArchTargetLowering : public TargetLowering {
                              Instruction *I = nullptr) const override;
 
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isZExtFree(SDValue Val, EVT VT2) const override;
   bool isSExtCheaperThanZExt(EVT SrcVT, EVT DstVT) const override;
 
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 68c80dd9aa5c76..2aaf7b24d52544 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -17300,7 +17300,8 @@ bool PPCTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<16>(Imm) || isUInt<16>(Imm);
 }
 
-bool PPCTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool PPCTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   return isInt<16>(Imm) || isUInt<16>(Imm);
 }
 
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 0bdfdcd15441f4..a1b404bd655b36 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -1023,7 +1023,8 @@ namespace llvm {
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     /// isTruncateFree - Return true if it's free to truncate a value of
     /// type Ty1 to type Ty2. e.g. On PPC it's free to truncate a i64 value in
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 4c3dc63afd878d..c6b02b8b26856b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -1783,8 +1783,9 @@ bool RISCVTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<12>(Imm);
 }
 
-bool RISCVTargetLowering::isLegalAddImmediate(int64_t Imm) const {
-  return isInt<12>(Imm);
+bool RISCVTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                              int64_t ScalableImm) const {
+  return !ScalableImm && isInt<12>(Imm);
 }
 
 // On RV32, 64-bit integers are split into their high and low parts and held
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index f90cb4df604761..61fb5809c9b8dd 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -475,7 +475,7 @@ class RISCVTargetLowering : public TargetLowering {
                              unsigned AS,
                              Instruction *I = nullptr) const override;
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isTruncateFree(Type *SrcTy, Type *DstTy) const override;
   bool isTruncateFree(EVT SrcVT, EVT DstVT) const override;
   bool isZExtFree(SDValue Val, EVT VT2) const override;
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 3b85a6ac0371ed..a2aa33ff059489 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -944,7 +944,8 @@ bool SystemZTargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<32>(Imm) || isUInt<32>(Imm);
 }
 
-bool SystemZTargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool SystemZTargetLowering::isLegalAddImmediate(int64_t Imm,
+                                                int64_t ScalableImm) const {
   // We can use ALGFI or SLGFI.
   return isUInt<32>(Imm) || isUInt<32>(-Imm);
 }
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index baf4ba41654879..274b23b4d28935 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -477,7 +477,7 @@ class SystemZTargetLowering : public TargetLowering {
   AtomicExpansionKind
   shouldExpandAtomicRMWInIR(AtomicRMWInst *RMW) const override;
   bool isLegalICmpImmediate(int64_t Imm) const override;
-  bool isLegalAddImmediate(int64_t Imm) const override;
+  bool isLegalAddImmediate(int64_t Imm, int64_t ScalableImm = 0) const override;
   bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty,
                              unsigned AS,
                              Instruction *I = nullptr) const override;
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6eaaec407dbb08..d5f5ef24e56d5d 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -33882,7 +33882,8 @@ bool X86TargetLowering::isLegalICmpImmediate(int64_t Imm) const {
   return isInt<32>(Imm);
 }
 
-bool X86TargetLowering::isLegalAddImmediate(int64_t Imm) const {
+bool X86TargetLowering::isLegalAddImmediate(int64_t Imm,
+                                            int64_t ScalableImm) const {
   // Can also use sub to handle negated immediates.
   return isInt<32>(Imm);
 }
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h b/llvm/lib/Target/X86/X86ISelLowering.h
index fe1943b5760844..b3cccee0e67bf1 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -1329,7 +1329,8 @@ namespace llvm {
     /// add immediate, that is the target has add instructions which can
     /// add a register and the immediate without having to materialize
     /// the immediate into a register.
-    bool isLegalAddImmediate(int64_t Imm) const override;
+    bool isLegalAddImmediate(int64_t Imm,
+                             int64_t ScalableImm = 0) const override;
 
     bool isLegalStoreImmediate(int64_t Imm) const override;
 
diff --git a/llvm/unittests/Target/AArch64/CMakeLists.txt b/llvm/unittests/Target/AArch64/CMakeLists.txt
index dacd919ba1e33b..64ab991ac479a4 100644
--- a/llvm/unittests/Target/AArch64/CMakeLists.txt
+++ b/llvm/unittests/Target/AArch64/CMakeLists.txt
@@ -29,6 +29,7 @@ add_llvm_target_unittest(AArch64Tests
   MatrixRegisterAliasing.cpp
   SMEAttributesTest.cpp
   AArch64SVESchedPseudoTest.cpp
+  Immediates.cpp
   )
 
 set_property(TARGET AArch64Tests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
diff --git a/llvm/unittests/Target/AArch64/Immediates.cpp b/llvm/unittests/Target/AArch64/Immediates.cpp
new file mode 100644
index 00000000000000..5f5895e44c6110
--- /dev/null
+++ b/llvm/unittests/Target/AArch64/Immediates.cpp
@@ -0,0 +1,75 @@
+#include "AArch64Subtarget.h"
+#include "AArch64TargetMachine.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+
+#include "gtest/gtest.h"
+#include <initializer_list>
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+struct TestCase {
+  int64_t FixedImm;
+  int64_t ScalableImm;
+  bool Result;
+};
+
+const std::initializer_list<TestCase> Tests = {
+    // FixedImm, ScalableImm, Result
+    // No change, easily 'supported'
+    {0, 0, true},
+
+    // Simple fixed immediate cases
+    // +8
+    {8, 0, true},
+    // -16
+    {-16, 0, true},
+
+    // Scalable; addvl increments by whole registers, range [-32,31]
+    // +(16 * vscale), one register's worth
+    {0, 16, true},
+    // +(8 * vscale), half a register's worth
+    {0, 8, false},
+    // -(32 * 16 * vscale)
+    {0, -512, true},
+    // -(33 * 16 * vscale)
+    {0, -528, false},
+    // +(31 * 16 * vscale)
+    {0, 496, true},
+    // +(32 * 16 * vscale)
+    {0, 512, false},
+
+    // Mixed; not supported.
+    // +(16 + (16 * vscale)) -- one register's worth + 16
+    {16, 16, false},
+};
+} // namespace
+
+TEST(Immediates, Immediates) {
+  LLVMInitializeAArch64TargetInfo();
+  LLVMInitializeAArch64Target();
+  LLVMInitializeAArch64TargetMC();
+
+  std::string Error;
+  auto TT = Triple::normalize("aarch64");
+  const Target *T = TargetRegistry::lookupTarget(TT, Error);
+
+  std::unique_ptr<TargetMachine> TM(
+      T->createTargetMachine(TT, "generic", "+sve", TargetOptions(),
+                             std::nullopt, std::nullopt,
+                             CodeGenOptLevel::Default));
+  AArch64Subtarget ST(TM->getTargetTriple(), TM->getTargetCPU(),
+                      TM->getTargetCPU(), TM->getTargetFeatureString(), *TM,
+                      true);
+
+  auto *TLI = ST.getTargetLowering();
+
+  for (const auto &Test : Tests) {
+    ASSERT_EQ(TLI->isLegalAddImmediate(Test.FixedImm, Test.ScalableImm),
+              Test.Result);
+  }
+}

Copy link

github-actions bot commented Mar 6, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@davemgreen
Copy link
Collaborator

Can you remind us where these functions are used? It might be simpler to have separate methods and move the scalable vs normal immediate check out a level.

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused as to how this will work - how do you indicate when to test for a legal scalar or scalable immediate? Suppose you want to know if 0 is legal?

@huntergr-arm
Copy link
Collaborator Author

Can you remind us where these functions are used? It might be simpler to have separate methods and move the scalable vs normal immediate check out a level.

I'm modifying these to handle the uses in LoopStrengthReduce, specifically for GenerateReassociationsImpl and GenerateCrossUseConstantOffsets.

Since we're just asking about a single immediate (rather than a whole addressing mode, as in PR #83255 ) then it's fine to make a second interface. Will do so.

Copy link
Collaborator

@davemgreen davemgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking sensible to me, I have some comments below from experimenting with some values.

Comment on lines 699 to 702
/// Return true if the specified immediate is legal add of a scalable
/// immediate, that is the target has add instructions which can add a
/// register with the immediate (multiplied by vscale) without having to
/// materialize the immediate into a register.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:
"Return true if the add of the specified scalable immediate is legal, that is..."

// +(16 * vscale), one register's worth
{16, true},
// +(8 * vscale), half a register's worth
{8, false},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 could be a inch if that wouldn't cause problems with your analysis. Same for 2 and 4.

@@ -16595,6 +16595,21 @@ bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed) const {
return IsLegal;
}

bool AArch64TargetLowering::isLegalAddScalableImmediate(int64_t Imm) const {
// Scalable immediates require SVE support.
if (!Subtarget->hasSVE())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should all of this require sve2? Otherwise we don't generate the instructions.

Adds an interface to determine whether an immediate would be legal within
an add instruction, when said immediate is multiplied by vscale.
@huntergr-arm huntergr-arm force-pushed the is-legal-add-immediate-scalable branch from 2a0017d to f2704d2 Compare March 14, 2024 13:37
Copy link
Collaborator

@davemgreen davemgreen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This looks sensible to me.

@huntergr-arm huntergr-arm merged commit 36a3f8f into llvm:main Mar 20, 2024
@huntergr-arm huntergr-arm deleted the is-legal-add-immediate-scalable branch March 20, 2024 10:28
chencha3 pushed a commit to chencha3/llvm-project that referenced this pull request Mar 23, 2024
…ate (llvm#84173)

Adds a second parameter (default to 0) to isLegalAddImmediate, to
represent a scalable immediate.

Extends the AArch64 implementation to match immediates based on what addvl and inc[h|w|d] support.
DavidSpickett added a commit to DavidSpickett/llvm-project that referenced this pull request May 2, 2024
llvm#84173 added uses
of std::labs on an int64_t which leads to this warning on
Arm 32 bit:
/home/david.spickett/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:16655:12: warning: absolute value function 'labs' given an argument of type 'long long' but has parameter of type 'long' which may cause truncation of value [-Wabsolute-value]
    return std::labs(Imm / 4) <= 16;
           ^
/home/david.spickett/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:16655:12: note: use function 'std::abs' instead
    return std::labs(Imm / 4) <= 16;
           ^~~~~~~~~
           std::abs

Since int64_t is "long long" on Arm, not "long".

Use std::abs instead since it has versions for "long" and "long long",
we'll pick up the right one at compile time
(https://en.cppreference.com/w/cpp/numeric/math/abs).
alanzhao1 pushed a commit to alanzhao1/llvm-project that referenced this pull request May 2, 2024
llvm#84173 added uses of std::labs
on an int64_t which leads to this warning on Arm 32 bit:
```
/home/david.spickett/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:16655:12: warning: absolute value function 'labs' given an argument of type 'long long' but has parameter of type 'long' which may cause truncation of value [-Wabsolute-value]
    return std::labs(Imm / 4) <= 16;
           ^
/home/david.spickett/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:16655:12: note: use function 'std::abs' instead
    return std::labs(Imm / 4) <= 16;
           ^~~~~~~~~
           std::abs
```

Since int64_t is "long long" on Arm, not "long".

Use std::abs instead since it has versions for "long" and "long long",
we'll pick up the right one at compile time
(https://en.cppreference.com/w/cpp/numeric/math/abs).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:AArch64 llvm:analysis Includes value tracking, cost tables and constant folding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants