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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions llvm/include/llvm/Analysis/TargetTransformInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,12 @@ class TargetTransformInfo {
/// 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
Expand Down Expand Up @@ -1835,6 +1841,7 @@ class TargetTransformInfo::Concept {
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,
Expand Down Expand Up @@ -2295,6 +2302,9 @@ class TargetTransformInfo::Model final : public TargetTransformInfo::Concept {
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);
}
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ class TargetTransformInfoImplBase {

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,
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/BasicTTIImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase<T> {
return getTLI()->isLegalAddImmediate(imm);
}

bool isLegalAddScalableImmediate(int64_t Imm) {
return getTLI()->isLegalAddScalableImmediate(Imm);
}

bool isLegalICmpImmediate(int64_t imm) {
return getTLI()->isLegalICmpImmediate(imm);
}
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,12 @@ class TargetLoweringBase {
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 {
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Analysis/TargetTransformInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,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);
}
Expand Down
32 changes: 32 additions & 0 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16595,6 +16595,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())
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.

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.
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ class AArch64TargetLowering : public TargetLowering {
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,
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/Target/AArch64/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 106 additions & 0 deletions llvm/unittests/Target/AArch64/Immediates.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
}