Skip to content
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

[RISCV] Implement RISCVInstrInfo::isAddImmediate #72356

Merged
merged 1 commit into from
Nov 16, 2023

Conversation

asb
Copy link
Contributor

@asb asb commented Nov 15, 2023

This hook is called by the target-independent implementation of TargetInstrInfo::describeLoadedValue. I've opted to test it via a C++ unit test, which although fiddly to set up seems the right way to test a function with such clear intended semantics (rather than testing the impact indirectly).

isAddImmediate will never recognise ADDIW as an add immediate which I think is conservatively correct, as the caller may not understand its semantics vs ADDI.

Note that although the doc comment for isAddImmediate specifies its behaviuor solely in terms of physical registers, none of the current in-tree implementations (including this one) bail out on virtual registers.

This hook is called by the target-independent implementation of
TargetInstrInfo::describeLoadedValue. I've opted to test it via a C++
unit test, which although fiddly to set up seems the right way to test a
function with such clear intended semantics (rather than testing the
impact indirectly).

isAddImmediate will never recognise ADDIW as an add immediate which I
_think_ is conservatively correct, as the caller may not understand its
semantics vs ADDI.

Note that although the doc comment for isAddImmediate specifies its
behaviuor solely in terms of physical registers, none of the current
in-tree implementations (including this one) bail out on virtual
registers.
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 15, 2023

@llvm/pr-subscribers-backend-risc-v

Author: Alex Bradbury (asb)

Changes

This hook is called by the target-independent implementation of TargetInstrInfo::describeLoadedValue. I've opted to test it via a C++ unit test, which although fiddly to set up seems the right way to test a function with such clear intended semantics (rather than testing the impact indirectly).

isAddImmediate will never recognise ADDIW as an add immediate which I think is conservatively correct, as the caller may not understand its semantics vs ADDI.

Note that although the doc comment for isAddImmediate specifies its behaviuor solely in terms of physical registers, none of the current in-tree implementations (including this one) bail out on virtual registers.


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

4 Files Affected:

  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+17)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.h (+3)
  • (modified) llvm/unittests/Target/RISCV/CMakeLists.txt (+5-1)
  • (added) llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp (+96)
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 9271f807a84838b..45df0b111752c21 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -2438,6 +2438,23 @@ MachineBasicBlock::iterator RISCVInstrInfo::insertOutlinedCall(
   return It;
 }
 
+std::optional<RegImmPair> RISCVInstrInfo::isAddImmediate(const MachineInstr &MI,
+                                                         Register Reg) const {
+  // TODO: Handle cases where Reg is a super- or sub-register of the
+  // destination register.
+  const MachineOperand &Op0 = MI.getOperand(0);
+  if (!Op0.isReg() || Reg != Op0.getReg())
+    return std::nullopt;
+
+  // Don't consider ADDIW as a candidate because the caller may not be aware
+  // of its sign extension behaviour.
+  if (MI.getOpcode() == RISCV::ADDI && MI.getOperand(1).isReg() &&
+      MI.getOperand(2).isImm())
+    return RegImmPair{MI.getOperand(1).getReg(), MI.getOperand(2).getImm()};
+
+  return std::nullopt;
+}
+
 // MIR printer helper function to annotate Operands with a comment.
 std::string RISCVInstrInfo::createMIROperandComment(
     const MachineInstr &MI, const MachineOperand &Op, unsigned OpIdx,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
index b33d8c28561596b..c2bffe1f9d6a1bf 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h
@@ -197,6 +197,9 @@ class RISCVInstrInfo : public RISCVGenInstrInfo {
                      MachineBasicBlock::iterator &It, MachineFunction &MF,
                      outliner::Candidate &C) const override;
 
+  std::optional<RegImmPair> isAddImmediate(const MachineInstr &MI,
+                                           Register Reg) const override;
+
   bool findCommutedOpIndices(const MachineInstr &MI, unsigned &SrcOpIdx1,
                              unsigned &SrcOpIdx2) const override;
   MachineInstr *commuteInstructionImpl(MachineInstr &MI, bool NewMI,
diff --git a/llvm/unittests/Target/RISCV/CMakeLists.txt b/llvm/unittests/Target/RISCV/CMakeLists.txt
index 9d0bf7244c02210..d80d29b7f0dadac 100644
--- a/llvm/unittests/Target/RISCV/CMakeLists.txt
+++ b/llvm/unittests/Target/RISCV/CMakeLists.txt
@@ -7,13 +7,17 @@ set(LLVM_LINK_COMPONENTS
   RISCVCodeGen
   RISCVDesc
   RISCVInfo
-  TargetParser
+  CodeGen
+  Core
   MC
+  SelectionDAG
+  TargetParser
   )
 
 add_llvm_target_unittest(RISCVTests
   MCInstrAnalysisTest.cpp
   RISCVBaseInfoTest.cpp
+  RISCVInstrInfoTest.cpp
   )
 
 set_property(TARGET RISCVTests PROPERTY FOLDER "Tests/UnitTests/TargetTests")
diff --git a/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
new file mode 100644
index 000000000000000..1e9a259a6b3f110
--- /dev/null
+++ b/llvm/unittests/Target/RISCV/RISCVInstrInfoTest.cpp
@@ -0,0 +1,96 @@
+//===- RISCVInstrInfoTest.cpp - RISCVInstrInfo unit tests -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RISCVInstrInfo.h"
+#include "RISCVSubtarget.h"
+#include "RISCVTargetMachine.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+class RISCVInstrInfoTest : public testing::TestWithParam<const char *> {
+protected:
+  std::unique_ptr<LLVMContext> Ctx;
+  std::unique_ptr<RISCVSubtarget> ST;
+  std::unique_ptr<MachineModuleInfo> MMI;
+  std::unique_ptr<MachineFunction> MF;
+
+  static void SetUpTestSuite() {
+    LLVMInitializeRISCVTargetInfo();
+    LLVMInitializeRISCVTarget();
+    LLVMInitializeRISCVTargetMC();
+  }
+
+  RISCVInstrInfoTest() {
+    std::string Error;
+    auto TT(Triple::normalize(GetParam()));
+    const Target *TheTarget = TargetRegistry::lookupTarget(TT, Error);
+    TargetOptions Options;
+
+    RISCVTargetMachine *TM = static_cast<RISCVTargetMachine *>(
+        TheTarget->createTargetMachine(TT, "generic", "", Options, std::nullopt,
+                                       std::nullopt, CodeGenOptLevel::Default));
+
+    Ctx = std::make_unique<LLVMContext>();
+    Module M("Module", *Ctx);
+    M.setDataLayout(TM->createDataLayout());
+    auto *FType = FunctionType::get(Type::getVoidTy(*Ctx), false);
+    auto *F = Function::Create(FType, GlobalValue::ExternalLinkage, "Test", &M);
+    MMI = std::make_unique<MachineModuleInfo>(TM);
+
+    ST = std::make_unique<RISCVSubtarget>(
+        TM->getTargetTriple(), TM->getTargetCPU(), TM->getTargetCPU(),
+        TM->getTargetFeatureString(),
+        TM->getTargetTriple().isArch64Bit() ? "lp64" : "ilp32", 0, 0, *TM);
+
+    MF = std::make_unique<MachineFunction>(*F, *TM, *ST, 42, *MMI);
+  }
+};
+
+TEST_P(RISCVInstrInfoTest, IsAddImmediate) {
+  const RISCVInstrInfo *TII = ST->getInstrInfo();
+  DebugLoc DL;
+
+  MachineInstr *MI1 = BuildMI(*MF, DL, TII->get(RISCV::ADDI), RISCV::X1)
+                          .addReg(RISCV::X2)
+                          .addImm(-128)
+                          .getInstr();
+  auto MI1Res = TII->isAddImmediate(*MI1, RISCV::X1);
+  ASSERT_TRUE(MI1Res.has_value());
+  EXPECT_EQ(MI1Res->Reg, RISCV::X2);
+  EXPECT_EQ(MI1Res->Imm, -128);
+  EXPECT_FALSE(TII->isAddImmediate(*MI1, RISCV::X2).has_value());
+
+  MachineInstr *MI2 =
+      BuildMI(*MF, DL, TII->get(RISCV::LUI), RISCV::X1).addImm(-128).getInstr();
+  EXPECT_FALSE(TII->isAddImmediate(*MI2, RISCV::X1));
+
+  // Check ADDIW isn't treated as isAddImmediate.
+  if (ST->is64Bit()) {
+    MachineInstr *MI3 = BuildMI(*MF, DL, TII->get(RISCV::ADDIW), RISCV::X1)
+                            .addReg(RISCV::X2)
+                            .addImm(-128)
+                            .getInstr();
+    EXPECT_FALSE(TII->isAddImmediate(*MI3, RISCV::X1));
+  }
+}
+
+} // namespace
+
+INSTANTIATE_TEST_SUITE_P(RV32And64, RISCVInstrInfoTest,
+                         testing::Values("riscv32", "riscv64"));

@asb
Copy link
Contributor Author

asb commented Nov 15, 2023

Note that although the doc comment for isAddImmediate specifies its behaviuor solely in terms of physical registers, none of the current in-tree implementations (including this one) bail out on virtual registers.

I've posted a doc change PR #72357 to adjust this (or for someone to tell me why that's the wrong thing to do!).


// Don't consider ADDIW as a candidate because the caller may not be aware
// of its sign extension behaviour.
if (MI.getOpcode() == RISCV::ADDI && MI.getOperand(1).isReg() &&
Copy link
Contributor

Choose a reason for hiding this comment

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

What circumstance that the third operand of ADDI is not an immediate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if we have such an instance. The AArch64 implementation is concerned that the third operand might be a global address but I haven't confirmed if that could happen for us. It just seemed sensible to code defensively.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to handle the LI case like Mips does in MipsInstrInfo::describeLoadedValue? I'm not sure returning X0 as the register is meaningful to the caller of isAddImmediate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do. I was hoping to land this change and then follow up with testing and improvements for describeLoadedValue. I'm not really sure whether landing this patch as-is would be considered to regress current behaviour or not. isAddImmediate was always returning false before so we weren't generating the DIExpression for these cases before, and even without the Mips special casing the handling of LI doesn't seem incorrect to my untrained eye (it's just that the reg+imm could be more efficiently represented as just imm in the case that reg==x0). I'm not a debuginfo expert so maybe there is a requirement that such expressions are simplified?

I think the LI case would be better handled in a target-independent way by having describeLoadedValue use getConstValDefinedInReg before then trying isAddImmediate. I'll take a look at how disruptive that change might be for other targets.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok thanks. I'm not debuginfo expert either. So this patch LGTM

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

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

LGTM

@asb asb merged commit 7f28e8c into llvm:main Nov 16, 2023
4 checks passed
@kda
Copy link
Contributor

kda commented Nov 16, 2023

@topperc
Copy link
Collaborator

topperc commented Nov 16, 2023

This appears to be breaking sanitizers: https://lab.llvm.org/buildbot/#/builders/168/builds/16852 https://lab.llvm.org/buildbot/#/builders/5/builds/38376

Since it's late for Alex, I'm working on a patch.

@asb
Copy link
Contributor Author

asb commented Nov 17, 2023

This appears to be breaking sanitizers: https://lab.llvm.org/buildbot/#/builders/168/builds/16852 https://lab.llvm.org/buildbot/#/builders/5/builds/38376

Since it's late for Alex, I'm working on a patch.

Thanks for that Craig, really appreciate it.

sr-tream pushed a commit to sr-tream/llvm-project that referenced this pull request Nov 20, 2023
This hook is called by the target-independent implementation of
TargetInstrInfo::describeLoadedValue. I've opted to test it via a C++
unit test, which although fiddly to set up seems the right way to test a
function with such clear intended semantics (rather than testing the
impact indirectly).

isAddImmediate will never recognise ADDIW as an add immediate which I
_think_ is conservatively correct, as the caller may not understand its
semantics vs ADDI.

Note that although the doc comment for isAddImmediate specifies its
behaviour solely in terms of physical registers, none of the current
in-tree implementations (including this one) bail out on virtual
registers (see llvm#72357).
zahiraam pushed a commit to zahiraam/llvm-project that referenced this pull request Nov 20, 2023
This hook is called by the target-independent implementation of
TargetInstrInfo::describeLoadedValue. I've opted to test it via a C++
unit test, which although fiddly to set up seems the right way to test a
function with such clear intended semantics (rather than testing the
impact indirectly).

isAddImmediate will never recognise ADDIW as an add immediate which I
_think_ is conservatively correct, as the caller may not understand its
semantics vs ADDI.

Note that although the doc comment for isAddImmediate specifies its
behaviour solely in terms of physical registers, none of the current
in-tree implementations (including this one) bail out on virtual
registers (see llvm#72357).
std::nullopt, CodeGenOptLevel::Default));

Ctx = std::make_unique<LLVMContext>();
Module M("Module", *Ctx);
Copy link
Contributor

Choose a reason for hiding this comment

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

The module is destroyed at the end of the constructor.

asb added a commit that referenced this pull request Dec 18, 2023
As pointed out by @s-barannikov in post-commit review of #72356, the
module is destroyed at the end of the constructor.
asb added a commit to asb/llvm-project that referenced this pull request Dec 20, 2023
Tests are in preparation for adding handling of the load of a constant
value as Mips does (noted in
<llvm#72356 (comment)>).

I've opted to implement these tests as a C++ unit test as on balance I
_think_ it's easier to follow and maintain than .mir tests trying to
indirectly test this function. That said, you see the limitations with
the test of describeLoadedValue on a memory operation where we'd rather
pass `MachinePointerInfo::getFixedStack` but can't because we'd need to
then ensure the necessary stack metadata for the function is present.
asb added a commit that referenced this pull request Jan 2, 2024
Tests are in preparation for adding handling of the load of a constant
value as Mips does (noted in

<#72356 (comment)>).

I've opted to implement these tests as a C++ unit test as on balance I
_think_ it's easier to follow and maintain than .mir tests trying to
indirectly test this function. That said, you see the limitations with
the test of describeLoadedValue on a memory operation where we'd rather
pass `MachinePointerInfo::getFixedStack` but can't because we'd need to
then ensure the necessary stack metadata for the function is present.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants