[TTI][TLI][AArch64] Support scalable immediates with isLegalAddImmediate (#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.
This commit is contained in:
@@ -696,6 +696,12 @@ public:
|
||||
/// immediate without having to materialize the immediate into a register.
|
||||
bool isLegalAddImmediate(int64_t Imm) const;
|
||||
|
||||
/// Return true if adding the specified scalable immediate is legal, 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.
|
||||
bool isLegalAddScalableImmediate(int64_t Imm) const;
|
||||
|
||||
/// Return true if the specified immediate is legal icmp immediate,
|
||||
/// that is the target has icmp instructions which can compare a register
|
||||
/// against the immediate without having to materialize the immediate into a
|
||||
@@ -1842,6 +1848,7 @@ public:
|
||||
std::function<void(Instruction *, unsigned, APInt, APInt &)>
|
||||
SimplifyAndSetOp) = 0;
|
||||
virtual bool isLegalAddImmediate(int64_t Imm) = 0;
|
||||
virtual bool isLegalAddScalableImmediate(int64_t Imm) = 0;
|
||||
virtual bool isLegalICmpImmediate(int64_t Imm) = 0;
|
||||
virtual bool isLegalAddressingMode(Type *Ty, GlobalValue *BaseGV,
|
||||
int64_t BaseOffset, bool HasBaseReg,
|
||||
@@ -2303,6 +2310,9 @@ public:
|
||||
bool isLegalAddImmediate(int64_t Imm) override {
|
||||
return Impl.isLegalAddImmediate(Imm);
|
||||
}
|
||||
bool isLegalAddScalableImmediate(int64_t Imm) override {
|
||||
return Impl.isLegalAddScalableImmediate(Imm);
|
||||
}
|
||||
bool isLegalICmpImmediate(int64_t Imm) override {
|
||||
return Impl.isLegalICmpImmediate(Imm);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,8 @@ public:
|
||||
|
||||
bool isLegalAddImmediate(int64_t Imm) const { return false; }
|
||||
|
||||
bool isLegalAddScalableImmediate(int64_t Imm) const { return false; }
|
||||
|
||||
bool isLegalICmpImmediate(int64_t Imm) const { return false; }
|
||||
|
||||
bool isLegalAddressingMode(Type *Ty, GlobalValue *BaseGV, int64_t BaseOffset,
|
||||
|
||||
@@ -328,6 +328,10 @@ public:
|
||||
return getTLI()->isLegalAddImmediate(imm);
|
||||
}
|
||||
|
||||
bool isLegalAddScalableImmediate(int64_t Imm) {
|
||||
return getTLI()->isLegalAddScalableImmediate(Imm);
|
||||
}
|
||||
|
||||
bool isLegalICmpImmediate(int64_t imm) {
|
||||
return getTLI()->isLegalICmpImmediate(imm);
|
||||
}
|
||||
|
||||
@@ -2772,6 +2772,12 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return true if adding the specified scalable immediate is legal, 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.
|
||||
virtual bool isLegalAddScalableImmediate(int64_t) const { return false; }
|
||||
|
||||
/// Return true if the specified immediate is legal for the value input of a
|
||||
/// store instruction.
|
||||
virtual bool isLegalStoreImmediate(int64_t Value) const {
|
||||
|
||||
@@ -396,6 +396,10 @@ bool TargetTransformInfo::isLegalAddImmediate(int64_t Imm) const {
|
||||
return TTIImpl->isLegalAddImmediate(Imm);
|
||||
}
|
||||
|
||||
bool TargetTransformInfo::isLegalAddScalableImmediate(int64_t Imm) const {
|
||||
return TTIImpl->isLegalAddScalableImmediate(Imm);
|
||||
}
|
||||
|
||||
bool TargetTransformInfo::isLegalICmpImmediate(int64_t Imm) const {
|
||||
return TTIImpl->isLegalICmpImmediate(Imm);
|
||||
}
|
||||
|
||||
@@ -16616,6 +16616,38 @@ bool AArch64TargetLowering::isLegalAddImmediate(int64_t Immed) const {
|
||||
return IsLegal;
|
||||
}
|
||||
|
||||
bool AArch64TargetLowering::isLegalAddScalableImmediate(int64_t Imm) const {
|
||||
// We will only emit addvl/inc* instructions for SVE2
|
||||
if (!Subtarget->hasSVE2())
|
||||
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 (Imm % 16 == 0)
|
||||
return isInt<6>(Imm / 16);
|
||||
|
||||
// Inc[b|h|w|d] instructions take a pattern and a positive immediate
|
||||
// multiplier. For now, assume a pattern of 'all'. Incb would be a subset
|
||||
// of addvl as a result, so only take h|w|d into account.
|
||||
// Dec[h|w|d] will cover subtractions.
|
||||
// Immediates are in the range [1,16], so we can't do a 2's complement check.
|
||||
// FIXME: Can we make use of other patterns to cover other immediates?
|
||||
|
||||
// inch|dech
|
||||
if (Imm % 8 == 0)
|
||||
return std::labs(Imm / 8) <= 16;
|
||||
// incw|decw
|
||||
if (Imm % 4 == 0)
|
||||
return std::labs(Imm / 4) <= 16;
|
||||
// incd|decd
|
||||
if (Imm % 2 == 0)
|
||||
return std::labs(Imm / 2) <= 16;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return false to prevent folding
|
||||
// (mul (add x, c1), c2) -> (add (mul x, c2), c2*c1) in DAGCombine,
|
||||
// if the folding leads to worse code.
|
||||
|
||||
@@ -689,6 +689,7 @@ public:
|
||||
StoreInst *SI) const override;
|
||||
|
||||
bool isLegalAddImmediate(int64_t) const override;
|
||||
bool isLegalAddScalableImmediate(int64_t) const override;
|
||||
bool isLegalICmpImmediate(int64_t) const override;
|
||||
|
||||
bool isMulAddWithConstProfitable(SDValue AddNode,
|
||||
|
||||
@@ -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")
|
||||
|
||||
106
llvm/unittests/Target/AArch64/Immediates.cpp
Normal file
106
llvm/unittests/Target/AArch64/Immediates.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#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 Imm;
|
||||
bool Result;
|
||||
};
|
||||
|
||||
const std::initializer_list<TestCase> Tests = {
|
||||
// ScalableImm, Result
|
||||
// No change, easily 'supported'
|
||||
{0, true},
|
||||
|
||||
// addvl increments by whole registers, range [-32,31]
|
||||
// +(16 * vscale), one register's worth
|
||||
{16, true},
|
||||
// -(32 * 16 * vscale)
|
||||
{-512, true},
|
||||
// -(33 * 16 * vscale)
|
||||
{-528, false},
|
||||
// +(31 * 16 * vscale)
|
||||
{496, true},
|
||||
// +(32 * 16 * vscale)
|
||||
{512, false},
|
||||
|
||||
// inc[h|w|d] increments by the number of 16/32/64bit elements in a
|
||||
// register. mult_imm is in the range [1,16]
|
||||
// +(mult_imm * num_elts * vscale)
|
||||
// +(1 * 8 * vscale), 16 bit
|
||||
{8, true},
|
||||
// +(15 * 8 * vscale), 16 bit
|
||||
{120, true},
|
||||
// +(1 * 4 * vscale), 32 bit
|
||||
{4, true},
|
||||
// +(7 * 4 * vscale), 32 bit
|
||||
{28, true},
|
||||
// +(1 * 2 * vscale), 64 bit
|
||||
{2, true},
|
||||
// +(13 * 2 * vscale), 64 bit
|
||||
{26, true},
|
||||
// +(17 * 8 * vscale), 16 bit, out of range.
|
||||
{136, false},
|
||||
// +(19 * 2 * vscale), 64 bit, out of range.
|
||||
{38, false},
|
||||
// +(21 * 4 * vscale), 32 bit, out of range.
|
||||
{84, false},
|
||||
|
||||
// dec[h|w|d] -- Same as above, but negative.
|
||||
// -(mult_imm * num_elts * vscale)
|
||||
// -(1 * 8 * vscale), 16 bit
|
||||
{-8, true},
|
||||
// -(15 * 8 * vscale), 16 bit
|
||||
{-120, true},
|
||||
// -(1 * 4 * vscale), 32 bit
|
||||
{-4, true},
|
||||
// -(7 * 4 * vscale), 32 bit
|
||||
{-28, true},
|
||||
// -(1 * 2 * vscale), 64 bit
|
||||
{-2, true},
|
||||
// -(13 * 2 * vscale), 64 bit
|
||||
{-26, true},
|
||||
// -(17 * 8 * vscale), 16 bit, out of range.
|
||||
{-136, false},
|
||||
// -(19 * 2 * vscale), 64 bit, out of range.
|
||||
{-38, false},
|
||||
// -(21 * 4 * vscale), 32 bit, out of range.
|
||||
{-84, false},
|
||||
|
||||
// Invalid; not divisible by the above powers of 2.
|
||||
{5, 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", "+sve2", 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->isLegalAddScalableImmediate(Test.Imm), Test.Result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user