[llvm] bc3eee1 - [Exegesis][RISCV] Add RISCV support for llvm-exegesis (#89047)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 18 03:53:35 PST 2024
Author: AnastasiyaChernikova
Date: 2024-12-18T14:53:30+03:00
New Revision: bc3eee11ea6f771bf007c4921a34c1dfee040471
URL: https://github.com/llvm/llvm-project/commit/bc3eee11ea6f771bf007c4921a34c1dfee040471
DIFF: https://github.com/llvm/llvm-project/commit/bc3eee11ea6f771bf007c4921a34c1dfee040471.diff
LOG: [Exegesis][RISCV] Add RISCV support for llvm-exegesis (#89047)
This patch also makes following amendments to core exegesis:
* Added distinction between regular registers aliasing check and
registers used as memory address in instruction.
* Added scratch memory space pointer register.
* General exegesis options were amended:
* mattr - new option to pass a list of enabled target features
Llvm-exegesis RISCV port is a result of team effort. Below everyone
involved listed.
Co-authored-by: Konstantin Vladimirov
<konstantin.vladimirov at syntacore.com>
Co-authored-by: Dmitrii Petrov <dmitrii.petrov at syntacore.com>
Co-authored-by: Dmitry Bushev <dmitry.bushev at syntacore.com>
Co-authored-by: Mark Goncharov <mark.goncharov at syntacore.com>
Co-authored-by: Anastasiya Chernikova
<anastasiya.chernikova at syntacore.com>
---------
Co-authored-by: Dmitry Bushev <dmitry.bushev at syntacore.com>
Added:
llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s
llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s
llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s
llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt
llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
Modified:
llvm/tools/llvm-exegesis/lib/CMakeLists.txt
llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp
llvm/tools/llvm-exegesis/lib/MCInstrDescView.h
llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
llvm/tools/llvm-exegesis/llvm-exegesis.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s
new file mode 100644
index 00000000000000..bdc02d4af21551
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s
@@ -0,0 +1,59 @@
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=AMOAND_D -mattr="+a" | FileCheck --check-prefix=AMOAND_D %s
+
+AMOAND_D: ---
+AMOAND_D-NEXT: mode: latency
+AMOAND_D-NEXT: key:
+AMOAND_D-NEXT: instructions:
+AMOAND_D-NEXT: - 'AMOAND_D [[RE01:X[0-9]+]] X10 [[RE01:X[0-9]+]]'
+AMOAND_D-NEXT: config: ''
+AMOAND_D-NEXT: register_initial_values:
+AMOAND_D-NEXT: - '[[RE01:X[0-9]+]]=0x0'
+AMOAND_D-DAG: ...
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=AMOADD_W -mattr="+a" | FileCheck --check-prefix=AMOADD_W %s
+
+AMOADD_W: ---
+AMOADD_W-NEXT: mode: latency
+AMOADD_W-NEXT: key:
+AMOADD_W-NEXT: instructions:
+AMOADD_W-NEXT: - 'AMOADD_W [[RE02:X[0-9]+]] X10 [[RE02:X[0-9]+]]'
+AMOADD_W-NEXT: config: ''
+AMOADD_W-NEXT: register_initial_values:
+AMOADD_W-NEXT: - '[[RE02:X[0-9]+]]=0x0'
+AMOADD_W-DAG: ...
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=AMOMAXU_D -mattr="+a" | FileCheck --check-prefix=AMOMAXU_D %s
+
+AMOMAXU_D: ---
+AMOMAXU_D-NEXT: mode: latency
+AMOMAXU_D-NEXT: key:
+AMOMAXU_D-NEXT: instructions:
+AMOMAXU_D-NEXT: - 'AMOMAXU_D [[RE03:X[0-9]+]] X10 [[RE03:X[0-9]+]]'
+AMOMAXU_D-NEXT: config: ''
+AMOMAXU_D-NEXT: register_initial_values:
+AMOMAXU_D-NEXT: - '[[RE03:X[0-9]+]]=0x0'
+AMOMAXU_D-DAG: ...
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=AMOMIN_W -mattr="+a" | FileCheck --check-prefix=AMOMIN_W %s
+
+AMOMIN_W: ---
+AMOMIN_W-NEXT: mode: latency
+AMOMIN_W-NEXT: key:
+AMOMIN_W-NEXT: instructions:
+AMOMIN_W-NEXT: - 'AMOMIN_W [[RE04:X[0-9]+]] X10 [[RE04:X[0-9]+]]'
+AMOMIN_W-NEXT: config: ''
+AMOMIN_W-NEXT: register_initial_values:
+AMOMIN_W-NEXT: - '[[RE04:X[0-9]+]]=0x0'
+AMOMIN_W-DAG: ...
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=AMOXOR_D -mattr="+a" | FileCheck --check-prefix=AMOXOR_D %s
+
+AMOXOR_D: ---
+AMOXOR_D-NEXT: mode: latency
+AMOXOR_D-NEXT: key:
+AMOXOR_D-NEXT: instructions:
+AMOXOR_D-NEXT: - 'AMOXOR_D [[RE05:X[0-9]+]] X10 [[RE05:X[0-9]+]]'
+AMOXOR_D-NEXT: config: ''
+AMOXOR_D-NEXT: register_initial_values:
+AMOXOR_D-NEXT: - '[[RE05:X[0-9]+]]=0x0'
+AMOXOR_D-DAG: ...
diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s
new file mode 100644
index 00000000000000..9e94f024ed1162
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s
@@ -0,0 +1,48 @@
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_ADDI -mattr=+c | FileCheck --check-prefix=C_ADDI %s
+
+C_ADDI: ---
+C_ADDI-NEXT: mode: latency
+C_ADDI-NEXT: key:
+C_ADDI-NEXT: instructions:
+C_ADDI-NEXT: - 'C_ADDI [[REG01:X[0-9]+]] [[RE02:X[0-9]+]] [[IMM0:i_0x[0-9]+]]'
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_ADDIW -mattr=+c | FileCheck --check-prefix=C_ADDIW %s
+
+C_ADDIW: ---
+C_ADDIW-NEXT: mode: latency
+C_ADDIW-NEXT: key:
+C_ADDIW-NEXT: instructions:
+C_ADDIW-NEXT: - 'C_ADDIW [[REG11:X[0-9]+]] [[RE12:X[0-9]+]] [[IMM1:i_0x[0-9]+]]'
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_ANDI -mattr=+c | FileCheck --check-prefix=C_ANDI %s
+
+C_ANDI: ---
+C_ANDI-NEXT: mode: latency
+C_ANDI-NEXT: key:
+C_ANDI-NEXT: instructions:
+C_ANDI-NEXT: - 'C_ANDI [[REG31:X[0-9]+]] [[REG32:X[0-9]+]] [[IMM3:i_0x[0-9]+]]'
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_SLLI -mattr=+c | FileCheck --check-prefix=C_SLLI %s
+
+C_SLLI: ---
+C_SLLI-NEXT: mode: latency
+C_SLLI-NEXT: key:
+C_SLLI-NEXT: instructions:
+C_SLLI-NEXT: - 'C_SLLI [[REG81:X[0-9]+]] [[REG82:X[0-9]+]] [[IMM8:i_0x[0-9]+]]'
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_SRAI -mattr=+c | FileCheck --check-prefix=C_SRAI %s
+
+C_SRAI: ---
+C_SRAI-NEXT: mode: latency
+C_SRAI-NEXT: key:
+C_SRAI-NEXT: instructions:
+C_SRAI-NEXT: - 'C_SRAI [[REG91:X[0-9]+]] [[REG92:X[0-9]+]] [[IMM9:i_0x[0-9]+]]'
+
+# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --mcpu=generic --benchmark-phase=assemble-measured-code -opcode-name=C_SRLI -mattr=+c | FileCheck --check-prefix=C_SRLI %s
+
+C_SRLI: ---
+C_SRLI-NEXT: mode: latency
+C_SRLI-NEXT: key:
+C_SRLI-NEXT: instructions:
+C_SRLI-NEXT: - 'C_SRLI [[REG101:X[0-9]+]] [[REG102:X[0-9]+]] [[IMM10:i_0x[0-9]+]]'
+C_SRLI-DAG: ...
diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s
new file mode 100644
index 00000000000000..2dea89cca4d7e9
--- /dev/null
+++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s
@@ -0,0 +1,11 @@
+# RUN: llvm-exegesis -mtriple=riscv64-unknown-linux-gnu --mcpu=generic -mode=latency --benchmark-phase=assemble-measured-code -mattr=+d -opcode-name=FADD_D | FileCheck %s
+
+CHECK: ---
+CHECK-NEXT: mode: latency
+CHECK-NEXT: key:
+CHECK-NEXT: instructions:
+CHECK-NEXT: - 'FADD_D [[REG1:F[0-9]+_D]] [[REG2:F[0-9]+_D]] [[REG3:F[0-9]+_D]] i_0x7'
+CHECK-NEXT: config: ''
+CHECK-NEXT: register_initial_values:
+CHECK-DAG: - '[[REG1]]=0x0'
+CHECK-DAG: ...
diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
index 414b49e5e021c2..d95c37ff5426bd 100644
--- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
+++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
@@ -12,6 +12,9 @@ endif()
if (LLVM_TARGETS_TO_BUILD MATCHES "Mips")
list(APPEND LLVM_EXEGESIS_TARGETS "Mips")
endif()
+if(LLVM_TARGETS_TO_BUILD MATCHES "RISCV")
+ list(APPEND LLVM_EXEGESIS_TARGETS "RISCV")
+endif()
set(LLVM_EXEGESIS_TARGETS ${LLVM_EXEGESIS_TARGETS} PARENT_SCOPE)
diff --git a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp
index 9c926d1fc61124..c9225e51213e59 100644
--- a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp
+++ b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp
@@ -95,11 +95,12 @@ Instruction::Instruction(const MCInstrDesc *Description, StringRef Name,
const BitVector *ImplDefRegs,
const BitVector *ImplUseRegs,
const BitVector *AllDefRegs,
- const BitVector *AllUseRegs)
+ const BitVector *AllUseRegs,
+ const BitVector *NonMemoryRegs)
: Description(*Description), Name(Name), Operands(std::move(Operands)),
Variables(std::move(Variables)), ImplDefRegs(*ImplDefRegs),
ImplUseRegs(*ImplUseRegs), AllDefRegs(*AllDefRegs),
- AllUseRegs(*AllUseRegs) {}
+ AllUseRegs(*AllUseRegs), NonMemoryRegs(*NonMemoryRegs) {}
std::unique_ptr<Instruction>
Instruction::create(const MCInstrInfo &InstrInfo,
@@ -166,6 +167,8 @@ Instruction::create(const MCInstrInfo &InstrInfo,
BitVector ImplUseRegs = RATC.emptyRegisters();
BitVector AllDefRegs = RATC.emptyRegisters();
BitVector AllUseRegs = RATC.emptyRegisters();
+ BitVector NonMemoryRegs = RATC.emptyRegisters();
+
for (const auto &Op : Operands) {
if (Op.isReg()) {
const auto &AliasingBits = Op.getRegisterAliasing().aliasedBits();
@@ -177,6 +180,8 @@ Instruction::create(const MCInstrInfo &InstrInfo,
ImplDefRegs |= AliasingBits;
if (Op.isUse() && Op.isImplicit())
ImplUseRegs |= AliasingBits;
+ if (Op.isUse() && !Op.isMemory())
+ NonMemoryRegs |= AliasingBits;
}
}
// Can't use make_unique because constructor is private.
@@ -185,7 +190,8 @@ Instruction::create(const MCInstrInfo &InstrInfo,
std::move(Variables), BVC.getUnique(std::move(ImplDefRegs)),
BVC.getUnique(std::move(ImplUseRegs)),
BVC.getUnique(std::move(AllDefRegs)),
- BVC.getUnique(std::move(AllUseRegs))));
+ BVC.getUnique(std::move(AllUseRegs)),
+ BVC.getUnique(std::move(NonMemoryRegs))));
}
const Operand &Instruction::getPrimaryOperand(const Variable &Var) const {
@@ -240,6 +246,12 @@ bool Instruction::hasAliasingRegisters(
ForbiddenRegisters);
}
+bool Instruction::hasAliasingNotMemoryRegisters(
+ const BitVector &ForbiddenRegisters) const {
+ return anyCommonExcludingForbidden(AllDefRegs, NonMemoryRegs,
+ ForbiddenRegisters);
+}
+
bool Instruction::hasOneUseOrOneDef() const {
return AllDefRegs.count() || AllUseRegs.count();
}
diff --git a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h
index f8ebc07d01f35e..d7712e21c32c1c 100644
--- a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h
+++ b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h
@@ -133,6 +133,12 @@ struct Instruction {
// aliasing Use and Def registers.
bool hasAliasingRegisters(const BitVector &ForbiddenRegisters) const;
+ // Whether this instruction is self aliasing through some registers.
+ // Repeating this instruction may execute sequentially by picking aliasing
+ // Def and Not Memory Use registers. It may also execute in parallel by
+ // picking non aliasing Def and Not Memory Use registers.
+ bool hasAliasingNotMemoryRegisters(const BitVector &ForbiddenRegisters) const;
+
// Whether this instruction's registers alias with OtherInstr's registers.
bool hasAliasingRegistersThrough(const Instruction &OtherInstr,
const BitVector &ForbiddenRegisters) const;
@@ -160,12 +166,15 @@ struct Instruction {
const BitVector &ImplUseRegs; // The set of aliased implicit use registers.
const BitVector &AllDefRegs; // The set of all aliased def registers.
const BitVector &AllUseRegs; // The set of all aliased use registers.
+ // The set of all aliased not memory use registers.
+ const BitVector &NonMemoryRegs;
+
private:
Instruction(const MCInstrDesc *Description, StringRef Name,
SmallVector<Operand, 8> Operands,
SmallVector<Variable, 4> Variables, const BitVector *ImplDefRegs,
const BitVector *ImplUseRegs, const BitVector *AllDefRegs,
- const BitVector *AllUseRegs);
+ const BitVector *AllUseRegs, const BitVector *NonMemoryRegs);
};
// Instructions are expensive to instantiate. This class provides a cache of
diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt
new file mode 100644
index 00000000000000..489ac6d6e34b33
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt
@@ -0,0 +1,22 @@
+include_directories(
+ ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV
+ ${LLVM_BINARY_DIR}/lib/Target/RISCV
+)
+
+set(LLVM_LINK_COMPONENTS
+ CodeGen
+ RISCV
+ Exegesis
+ Core
+ Support
+ )
+
+add_llvm_library(LLVMExegesisRISCV
+ DISABLE_LLVM_LINK_LLVM_DYLIB
+ STATIC
+ Target.cpp
+
+ DEPENDS
+ intrinsics_gen
+ RISCVCommonTableGen
+ )
diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
new file mode 100644
index 00000000000000..891818b625fe14
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp
@@ -0,0 +1,275 @@
+//===-- Target.cpp ----------------------------------------------*- C++ -*-===//
+//
+// 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 "../Target.h"
+
+#include "MCTargetDesc/RISCVBaseInfo.h"
+#include "MCTargetDesc/RISCVMCTargetDesc.h"
+#include "MCTargetDesc/RISCVMatInt.h"
+#include "RISCVInstrInfo.h"
+
+// include computeAvailableFeatures and computeRequiredFeatures.
+#define GET_AVAILABLE_OPCODE_CHECKER
+#include "RISCVGenInstrInfo.inc"
+
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+
+#include <vector>
+
+namespace llvm {
+namespace exegesis {
+
+namespace {
+
+// Stores constant value to a general-purpose (integer) register.
+static std::vector<MCInst> loadIntReg(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) {
+ SmallVector<MCInst, 8> MCInstSeq;
+ std::vector<MCInst> MatIntInstrs;
+ MCRegister DestReg = Reg;
+
+ RISCVMatInt::generateMCInstSeq(Value.getSExtValue(), STI, DestReg, MCInstSeq);
+ MatIntInstrs.resize(MCInstSeq.size());
+ std::copy(MCInstSeq.begin(), MCInstSeq.end(), MatIntInstrs.begin());
+
+ return MatIntInstrs;
+}
+
+const unsigned ScratchIntReg = RISCV::X30; // t5
+
+// Stores constant bits to a floating-point register.
+static std::vector<MCInst> loadFPRegBits(const MCSubtargetInfo &STI,
+ unsigned Reg, const APInt &Bits,
+ unsigned FmvOpcode) {
+ std::vector<MCInst> Instrs = loadIntReg(STI, ScratchIntReg, Bits);
+ Instrs.push_back(MCInstBuilder(FmvOpcode).addReg(Reg).addReg(ScratchIntReg));
+ return Instrs;
+}
+
+// main idea is:
+// we support APInt only if (represented as double) it has zero fractional
+// part: 1.0, 2.0, 3.0, etc... then we can do the trick: write int to tmp reg t5
+// and then do FCVT this is only reliable thing in 32-bit mode, otherwise we
+// need to use __floatsidf
+static std::vector<MCInst> loadFP64RegBits32(const MCSubtargetInfo &STI,
+ unsigned Reg, const APInt &Bits) {
+ double D = Bits.bitsToDouble();
+ double IPart;
+ double FPart = std::modf(D, &IPart);
+
+ if (std::abs(FPart) > std::numeric_limits<double>::epsilon()) {
+ errs() << "loadFP64RegBits32 is not implemented for doubles like " << D
+ << ", please remove fractional part\n";
+ return {};
+ }
+
+ std::vector<MCInst> Instrs = loadIntReg(STI, ScratchIntReg, Bits);
+ Instrs.push_back(
+ MCInstBuilder(RISCV::FCVT_D_W).addReg(Reg).addReg(ScratchIntReg));
+ return Instrs;
+}
+
+static MCInst nop() {
+ // ADDI X0, X0, 0
+ return MCInstBuilder(RISCV::ADDI)
+ .addReg(RISCV::X0)
+ .addReg(RISCV::X0)
+ .addImm(0);
+}
+
+static bool isVectorRegList(unsigned Reg) {
+ return RISCV::VRM2RegClass.contains(Reg) ||
+ RISCV::VRM4RegClass.contains(Reg) ||
+ RISCV::VRM8RegClass.contains(Reg) ||
+ RISCV::VRN2M1RegClass.contains(Reg) ||
+ RISCV::VRN2M2RegClass.contains(Reg) ||
+ RISCV::VRN2M4RegClass.contains(Reg) ||
+ RISCV::VRN3M1RegClass.contains(Reg) ||
+ RISCV::VRN3M2RegClass.contains(Reg) ||
+ RISCV::VRN4M1RegClass.contains(Reg) ||
+ RISCV::VRN4M2RegClass.contains(Reg) ||
+ RISCV::VRN5M1RegClass.contains(Reg) ||
+ RISCV::VRN6M1RegClass.contains(Reg) ||
+ RISCV::VRN7M1RegClass.contains(Reg) ||
+ RISCV::VRN8M1RegClass.contains(Reg);
+}
+
+class ExegesisRISCVTarget : public ExegesisTarget {
+public:
+ ExegesisRISCVTarget();
+
+ bool matchesArch(Triple::ArchType Arch) const override;
+
+ std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, unsigned Reg,
+ const APInt &Value) const override;
+
+ unsigned getDefaultLoopCounterRegister(const Triple &) const override;
+
+ void decrementLoopCounterAndJump(MachineBasicBlock &MBB,
+ MachineBasicBlock &TargetMBB,
+ const MCInstrInfo &MII,
+ unsigned LoopRegister) const override;
+
+ unsigned getScratchMemoryRegister(const Triple &TT) const override;
+
+ void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const override;
+
+ ArrayRef<unsigned> getUnavailableRegisters() const override;
+
+ Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var,
+ MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) const override;
+
+ std::vector<InstructionTemplate>
+ generateInstructionVariants(const Instruction &Instr,
+ unsigned MaxConfigsPerOpcode) const override;
+};
+
+ExegesisRISCVTarget::ExegesisRISCVTarget()
+ : ExegesisTarget(ArrayRef<CpuAndPfmCounters>{},
+ RISCV_MC::isOpcodeAvailable) {}
+
+#define GET_REGISTER_MATCHER
+#include "RISCVGenAsmMatcher.inc"
+
+bool ExegesisRISCVTarget::matchesArch(Triple::ArchType Arch) const {
+ return Arch == Triple::riscv32 || Arch == Triple::riscv64;
+}
+
+std::vector<MCInst> ExegesisRISCVTarget::setRegTo(const MCSubtargetInfo &STI,
+ unsigned Reg,
+ const APInt &Value) const {
+ if (RISCV::GPRRegClass.contains(Reg))
+ return loadIntReg(STI, Reg, Value);
+ if (RISCV::FPR16RegClass.contains(Reg))
+ return loadFPRegBits(STI, Reg, Value, RISCV::FMV_H_X);
+ if (RISCV::FPR32RegClass.contains(Reg))
+ return loadFPRegBits(STI, Reg, Value, RISCV::FMV_W_X);
+ if (RISCV::FPR64RegClass.contains(Reg)) {
+ if (STI.hasFeature(RISCV::Feature64Bit))
+ return loadFPRegBits(STI, Reg, Value, RISCV::FMV_D_X);
+ return loadFP64RegBits32(STI, Reg, Value);
+ }
+ if (Reg == RISCV::FRM || Reg == RISCV::VL || Reg == RISCV::VLENB ||
+ Reg == RISCV::VTYPE || RISCV::GPRPairRegClass.contains(Reg) ||
+ RISCV::VRRegClass.contains(Reg) || isVectorRegList(Reg)) {
+ // Don't initialize:
+ // - FRM
+ // - VL, VLENB, VTYPE
+ // - vector registers (and vector register lists)
+ // - Zfinx registers
+ // Generate 'NOP' so that exegesis treats such registers as initialized
+ // (it tries to initialize them with '0' anyway).
+ return {nop()};
+ }
+ errs() << "setRegTo is not implemented for Reg " << Reg
+ << ", results will be unreliable\n";
+ return {};
+}
+
+const unsigned DefaultLoopCounterReg = RISCV::X31; // t6
+const unsigned ScratchMemoryReg = RISCV::X10; // a0
+
+unsigned
+ExegesisRISCVTarget::getDefaultLoopCounterRegister(const Triple &) const {
+ return DefaultLoopCounterReg;
+}
+
+void ExegesisRISCVTarget::decrementLoopCounterAndJump(
+ MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB,
+ const MCInstrInfo &MII, unsigned LoopRegister) const {
+ BuildMI(&MBB, DebugLoc(), MII.get(RISCV::ADDI))
+ .addDef(LoopRegister)
+ .addUse(LoopRegister)
+ .addImm(-1);
+ BuildMI(&MBB, DebugLoc(), MII.get(RISCV::BNE))
+ .addUse(LoopRegister)
+ .addUse(RISCV::X0)
+ .addMBB(&TargetMBB);
+}
+
+unsigned ExegesisRISCVTarget::getScratchMemoryRegister(const Triple &TT) const {
+ return ScratchMemoryReg; // a0
+}
+
+void ExegesisRISCVTarget::fillMemoryOperands(InstructionTemplate &IT,
+ unsigned Reg,
+ unsigned Offset) const {
+ // TODO: for now we ignore Offset because have no way
+ // to detect it in instruction.
+ auto &I = IT.getInstr();
+
+ auto MemOpIt =
+ find_if(I.Operands, [](const Operand &Op) { return Op.isMemory(); });
+ assert(MemOpIt != I.Operands.end() &&
+ "Instruction must have memory operands");
+
+ const Operand &MemOp = *MemOpIt;
+
+ assert(MemOp.isReg() && "Memory operand expected to be register");
+
+ IT.getValueFor(MemOp) = MCOperand::createReg(Reg);
+}
+
+const unsigned UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg,
+ ScratchIntReg, ScratchMemoryReg};
+
+ArrayRef<unsigned> ExegesisRISCVTarget::getUnavailableRegisters() const {
+ return UnavailableRegisters;
+}
+
+Error ExegesisRISCVTarget::randomizeTargetMCOperand(
+ const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue,
+ const BitVector &ForbiddenRegs) const {
+ uint8_t OperandType =
+ Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType;
+
+ switch (OperandType) {
+ case RISCVOp::OPERAND_FRMARG:
+ AssignedValue = MCOperand::createImm(RISCVFPRndMode::DYN);
+ break;
+ case RISCVOp::OPERAND_SIMM10_LSB0000_NONZERO:
+ AssignedValue = MCOperand::createImm(0b1 << 4);
+ break;
+ case RISCVOp::OPERAND_SIMM6_NONZERO:
+ case RISCVOp::OPERAND_UIMMLOG2XLEN_NONZERO:
+ AssignedValue = MCOperand::createImm(1);
+ break;
+ default:
+ if (OperandType >= RISCVOp::OPERAND_FIRST_RISCV_IMM &&
+ OperandType <= RISCVOp::OPERAND_LAST_RISCV_IMM)
+ AssignedValue = MCOperand::createImm(0);
+ }
+ return Error::success();
+}
+
+std::vector<InstructionTemplate>
+ExegesisRISCVTarget::generateInstructionVariants(
+ const Instruction &Instr, unsigned int MaxConfigsPerOpcode) const {
+ InstructionTemplate IT{&Instr};
+ for (const Operand &Op : Instr.Operands)
+ if (Op.isMemory()) {
+ IT.getValueFor(Op) = MCOperand::createReg(ScratchMemoryReg);
+ }
+ return {IT};
+}
+
+} // anonymous namespace
+
+static ExegesisTarget *getTheRISCVExegesisTarget() {
+ static ExegesisRISCVTarget Target;
+ return &Target;
+}
+
+void InitializeRISCVExegesisTarget() {
+ ExegesisTarget::registerTarget(getTheRISCVExegesisTarget());
+}
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
index 7100b51bbb7298..9573e2242ad3f7 100644
--- a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp
@@ -54,12 +54,6 @@ computeAliasingInstructions(const LLVMState &State, const Instruction *Instr,
continue;
const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode);
const MCInstrDesc &OtherInstrDesc = OtherInstr.Description;
- // Ignore instructions that we cannot run.
- if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() ||
- OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() ||
- OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) {
- continue;
- }
if (OtherInstr.hasMemoryOperands())
continue;
if (!ET.allowAsBackToBack(OtherInstr))
@@ -81,12 +75,10 @@ static ExecutionMode getExecutionModes(const Instruction &Instr,
EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS;
if (Instr.hasMemoryOperands())
EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR;
- else {
- if (Instr.hasAliasingRegisters(ForbiddenRegisters))
- EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
- if (Instr.hasOneUseOrOneDef())
- EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
- }
+ if (Instr.hasAliasingNotMemoryRegisters(ForbiddenRegisters))
+ EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS;
+ if (Instr.hasOneUseOrOneDef())
+ EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR;
return EM;
}
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp b/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
index b37999ab017f59..282bc8ca912492 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp
@@ -37,10 +37,10 @@ namespace {
// An MCStreamer that reads a BenchmarkCode definition from a file.
class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
public:
- explicit BenchmarkCodeStreamer(MCContext *Context, const LLVMState &State,
+ explicit BenchmarkCodeStreamer(const ExegesisTarget &Target,
+ MCContext *Context, const LLVMState &State,
BenchmarkCode *Result)
- : MCStreamer(*Context), State(State), Result(Result) {}
-
+ : MCStreamer(*Context), Target(Target), State(State), Result(Result) {}
// Implementation of the MCStreamer interface. We only care about
// instructions.
void emitInstruction(const MCInst &Instruction,
@@ -218,6 +218,7 @@ class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
return *RegisterNumber;
}
+ const ExegesisTarget &Target;
const LLVMState &State;
BenchmarkCode *const Result;
unsigned InvalidComments = 0;
@@ -251,7 +252,8 @@ Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false));
Context.setObjectFileInfo(ObjectFileInfo.get());
Context.initInlineSourceManager();
- BenchmarkCodeStreamer Streamer(&Context, State, &Result);
+ BenchmarkCodeStreamer Streamer(State.getExegesisTarget(), &Context, State,
+ &Result);
std::string Error;
raw_string_ostream ErrorStream(Error);
diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
index 7dcff60a8fd11f..48357d443f713e 100644
--- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
+++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp
@@ -73,6 +73,9 @@ Error SnippetGenerator::generateConfigurations(
for (CodeTemplate &CT : Templates) {
// TODO: Generate as many BenchmarkCode as needed.
{
+ CT.ScratchSpacePointerInReg =
+ State.getExegesisTarget().getScratchMemoryRegister(
+ State.getTargetMachine().getTargetTriple());
BenchmarkCode BC;
BC.Info = CT.Info;
BC.Key.Instructions.reserve(CT.Instructions.size());
@@ -108,6 +111,12 @@ std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues(
// Loop invariant: DefinedRegs[i] is true iif it has been set at least once
// before the current instruction.
BitVector DefinedRegs = State.getRATC().emptyRegisters();
+ // If target always expects a scratch memory register as live input,
+ // mark it as defined.
+ const ExegesisTarget &Target = State.getExegesisTarget();
+ unsigned ScratchMemoryReg = Target.getScratchMemoryRegister(
+ State.getTargetMachine().getTargetTriple());
+ DefinedRegs.set(ScratchMemoryReg);
std::vector<RegisterValue> RIV;
for (const InstructionTemplate &IT : Instructions) {
// Returns the register that this Operand sets or uses, or 0 if this is not
@@ -200,7 +209,8 @@ static void setRegisterOperandValue(const RegisterOperandAssignment &ROV,
if (ROV.Op->isExplicit()) {
auto &AssignedValue = IB.getValueFor(*ROV.Op);
if (AssignedValue.isValid()) {
- assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg);
+ // TODO don't re-assign register operands which are already "locked"
+ // by Target in corresponding InstructionTemplate
return;
}
AssignedValue = MCOperand::createReg(ROV.Reg);
diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
index 546ec770a8d221..fa37e05956be8c 100644
--- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
+++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp
@@ -274,6 +274,10 @@ static cl::opt<int> BenchmarkProcessCPU(
cl::desc("The CPU number that the benchmarking process should executon on"),
cl::cat(BenchmarkOptions), cl::init(-1));
+static cl::opt<std::string> MAttr(
+ "mattr", cl::desc("comma-separated list of target architecture features"),
+ cl::value_desc("+feature1,-feature2,..."), cl::cat(Options), cl::init(""));
+
static ExitOnError ExitOnErr("llvm-exegesis error: ");
// Helper function that logs the error(s) and exits.
@@ -296,6 +300,18 @@ T ExitOnFileError(const Twine &FileName, Expected<T> &&E) {
return std::move(*E);
}
+static const char *getIgnoredOpcodeReasonOrNull(const LLVMState &State,
+ unsigned Opcode) {
+ const MCInstrDesc &InstrDesc = State.getIC().getInstr(Opcode).Description;
+ if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook())
+ return "Unsupported opcode: isPseudo/usesCustomInserter";
+ if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
+ return "Unsupported opcode: isBranch/isIndirectBranch";
+ if (InstrDesc.isCall() || InstrDesc.isReturn())
+ return "Unsupported opcode: isCall/isReturn";
+ return nullptr;
+}
+
// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided,
// and returns the opcode indices or {} if snippets should be read from
// `SnippetsFile`.
@@ -334,6 +350,7 @@ static std::vector<unsigned> getOpcodesOrDie(const LLVMState &State) {
return I->getSecond();
return 0u;
};
+
SmallVector<StringRef, 2> Pieces;
StringRef(OpcodeNames.getValue())
.split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);
@@ -352,17 +369,11 @@ static std::vector<unsigned> getOpcodesOrDie(const LLVMState &State) {
static Expected<std::vector<BenchmarkCode>>
generateSnippets(const LLVMState &State, unsigned Opcode,
const BitVector &ForbiddenRegs) {
- const Instruction &Instr = State.getIC().getInstr(Opcode);
- const MCInstrDesc &InstrDesc = Instr.Description;
// Ignore instructions that we cannot run.
- if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook())
- return make_error<Failure>(
- "Unsupported opcode: isPseudo/usesCustomInserter");
- if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch())
- return make_error<Failure>("Unsupported opcode: isBranch/isIndirectBranch");
- if (InstrDesc.isCall() || InstrDesc.isReturn())
- return make_error<Failure>("Unsupported opcode: isCall/isReturn");
+ if (const char *Reason = getIgnoredOpcodeReasonOrNull(State, Opcode))
+ return make_error<Failure>(Reason);
+ const Instruction &Instr = State.getIC().getInstr(Opcode);
const std::vector<InstructionTemplate> InstructionVariants =
State.getExegesisTarget().generateInstructionVariants(
Instr, MaxConfigsPerOpcode);
@@ -485,8 +496,8 @@ void benchmarkMain() {
LLVMInitialize##TargetName##AsmParser();
#include "llvm/Config/TargetExegesis.def"
- const LLVMState State =
- ExitOnErr(LLVMState::Create(TripleName, MCPU, "", UseDummyPerfCounters));
+ const LLVMState State = ExitOnErr(
+ LLVMState::Create(TripleName, MCPU, MAttr, UseDummyPerfCounters));
// Preliminary check to ensure features needed for requested
// benchmark mode are present on target CPU and/or OS.
More information about the llvm-commits
mailing list