let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS16ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def u16imm64 : Operand<i64> {
let PrintMethod = "printU16ImmOperand";
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCU16ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def s17imm64 : Operand<i64> {
// This operand type is used for addis/lis to allow the assembler parser
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS17ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def tocentry : Operand<iPTR> {
let MIOperandInfo = (ops i64imm:$imm);
def u1imm : Operand<i32> {
let PrintMethod = "printU1ImmOperand";
let ParserMatchClass = PPCU1ImmAsmOperand;
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU2ImmAsmOperand : AsmOperandClass {
def u2imm : Operand<i32> {
let PrintMethod = "printU2ImmOperand";
let ParserMatchClass = PPCU2ImmAsmOperand;
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCATBitsAsHintAsmOperand : AsmOperandClass {
def atimm : Operand<i32> {
let PrintMethod = "printATBitsAsHint";
let ParserMatchClass = PPCATBitsAsHintAsmOperand;
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU3ImmAsmOperand : AsmOperandClass {
def u3imm : Operand<i32> {
let PrintMethod = "printU3ImmOperand";
let ParserMatchClass = PPCU3ImmAsmOperand;
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU4ImmAsmOperand : AsmOperandClass {
def u4imm : Operand<i32> {
let PrintMethod = "printU4ImmOperand";
let ParserMatchClass = PPCU4ImmAsmOperand;
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS5ImmAsmOperand : AsmOperandClass {
let Name = "S5Imm"; let PredicateMethod = "isS5Imm";
let PrintMethod = "printS5ImmOperand";
let ParserMatchClass = PPCS5ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<5>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU5ImmAsmOperand : AsmOperandClass {
let Name = "U5Imm"; let PredicateMethod = "isU5Imm";
let PrintMethod = "printU5ImmOperand";
let ParserMatchClass = PPCU5ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<5>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU6ImmAsmOperand : AsmOperandClass {
let Name = "U6Imm"; let PredicateMethod = "isU6Imm";
let PrintMethod = "printU6ImmOperand";
let ParserMatchClass = PPCU6ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<6>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU7ImmAsmOperand : AsmOperandClass {
let Name = "U7Imm"; let PredicateMethod = "isU7Imm";
let PrintMethod = "printU7ImmOperand";
let ParserMatchClass = PPCU7ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<7>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU8ImmAsmOperand : AsmOperandClass {
let Name = "U8Imm"; let PredicateMethod = "isU8Imm";
let PrintMethod = "printU8ImmOperand";
let ParserMatchClass = PPCU8ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<8>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU10ImmAsmOperand : AsmOperandClass {
let Name = "U10Imm"; let PredicateMethod = "isU10Imm";
let PrintMethod = "printU10ImmOperand";
let ParserMatchClass = PPCU10ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<10>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU12ImmAsmOperand : AsmOperandClass {
let Name = "U12Imm"; let PredicateMethod = "isU12Imm";
let PrintMethod = "printU12ImmOperand";
let ParserMatchClass = PPCU12ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<12>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS16ImmAsmOperand : AsmOperandClass {
let Name = "S16Imm"; let PredicateMethod = "isS16Imm";
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS16ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU16ImmAsmOperand : AsmOperandClass {
let Name = "U16Imm"; let PredicateMethod = "isU16Imm";
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCU16ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS17ImmAsmOperand : AsmOperandClass {
let Name = "S17Imm"; let PredicateMethod = "isS17Imm";
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS17ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS34ImmAsmOperand : AsmOperandClass {
let Name = "S34Imm";
let EncoderMethod = "getImm34EncodingNoPCRel";
let ParserMatchClass = PPCS34ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<34>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def s34imm_pcrel : Operand<i64> {
let PrintMethod = "printS34ImmOperand";
let EncoderMethod = "getImm34EncodingPCRel";
let ParserMatchClass = PPCS34ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<34>";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def PPCImmZeroAsmOperand : AsmOperandClass {
let Name = "ImmZero";
let PrintMethod = "printImmZeroOperand";
let ParserMatchClass = PPCImmZeroAsmOperand;
let DecoderMethod = "decodeImmZeroOperand";
+ let OperandType = "OPERAND_IMMEDIATE";
}
def fpimm0 : PatLeaf<(fpimm), [{ return N->isExactlyValue(+0.0); }]>;
let MIOperandInfo = (ops dispRI:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getMemRIEncoding";
let DecoderMethod = "decodeMemRIOperands";
+ let OperandType = "OPERAND_MEMORY";
}
def memrr : Operand<iPTR> {
let PrintMethod = "printMemRegReg";
let MIOperandInfo = (ops ptr_rc_nor0:$ptrreg, ptr_rc_idx:$offreg);
+ let OperandType = "OPERAND_MEMORY";
}
def memrix : Operand<iPTR> { // memri where the imm is 4-aligned.
let PrintMethod = "printMemRegImm";
let MIOperandInfo = (ops dispRIX:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getMemRIXEncoding";
let DecoderMethod = "decodeMemRIXOperands";
+ let OperandType = "OPERAND_MEMORY";
}
def memrix16 : Operand<iPTR> { // memri, imm is 16-aligned, 12-bit, Inst{16:27}
let PrintMethod = "printMemRegImm";
let MIOperandInfo = (ops dispRIX16:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getMemRIX16Encoding";
let DecoderMethod = "decodeMemRIX16Operands";
+ let OperandType = "OPERAND_MEMORY";
}
def spe8dis : Operand<iPTR> { // SPE displacement where the imm is 8-aligned.
let PrintMethod = "printMemRegImm";
let MIOperandInfo = (ops dispSPE8:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getSPE8DisEncoding";
let DecoderMethod = "decodeSPE8Operands";
+ let OperandType = "OPERAND_MEMORY";
}
def spe4dis : Operand<iPTR> { // SPE displacement where the imm is 4-aligned.
let PrintMethod = "printMemRegImm";
let MIOperandInfo = (ops dispSPE4:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getSPE4DisEncoding";
let DecoderMethod = "decodeSPE4Operands";
+ let OperandType = "OPERAND_MEMORY";
}
def spe2dis : Operand<iPTR> { // SPE displacement where the imm is 2-aligned.
let PrintMethod = "printMemRegImm";
let MIOperandInfo = (ops dispSPE2:$imm, ptr_rc_nor0:$reg);
let EncoderMethod = "getSPE2DisEncoding";
let DecoderMethod = "decodeSPE2Operands";
+ let OperandType = "OPERAND_MEMORY";
}
// A single-register address. This is used with the SjLj
// G8RC_NOX0 registers.
def memr : Operand<iPTR> {
let MIOperandInfo = (ops ptr_rc_nor0:$ptrreg);
+ let OperandType = "OPERAND_MEMORY";
}
def PPCTLSRegOperand : AsmOperandClass {
let Name = "TLSReg"; let PredicateMethod = "isTLSReg";
namespace llvm {
namespace exegesis {
+// Helper to fill a memory operand with a value.
+static void setMemOp(InstructionTemplate &IT, int OpIdx,
+ const MCOperand &OpVal) {
+ const auto Op = IT.getInstr().Operands[OpIdx];
+ assert(Op.isExplicit() && "invalid memory pattern");
+ IT.getValueFor(Op) = OpVal;
+}
+
#include "PPCGenExegesis.inc"
namespace {
bool matchesArch(Triple::ArchType Arch) const override {
return Arch == Triple::ppc64le;
}
+ unsigned getScratchMemoryRegister(const Triple &) const override;
+ void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg,
+ unsigned Offset) const override;
};
} // end anonymous namespace
const APInt &Value) {
if (Value.getBitWidth() > RegBitWidth)
llvm_unreachable("Value must fit in the Register");
+ // We don't really care the value in reg, ignore the 16 bit
+ // restriction for now.
+ // TODO: make sure we get the exact value in reg if needed.
return MCInstBuilder(getLoadImmediateOpcode(RegBitWidth))
.addReg(Reg)
.addImm(Value.getZExtValue());
}
+unsigned
+ExegesisPowerPCTarget::getScratchMemoryRegister(const Triple &TT) const {
+ // R13 is reserved as Thread Pointer, we won't use threading in benchmark, so
+ // use it as scratch memory register
+ return TT.isArch64Bit() ? PPC::X13 : PPC::R13;
+}
+
+void ExegesisPowerPCTarget::fillMemoryOperands(InstructionTemplate &IT,
+ unsigned Reg,
+ unsigned Offset) const {
+ int MemOpIdx = 0;
+ if (IT.getInstr().hasTiedRegisters())
+ MemOpIdx = 1;
+ int DispOpIdx = MemOpIdx + 1;
+ const auto DispOp = IT.getInstr().Operands[DispOpIdx];
+ if (DispOp.isReg())
+ // We don't really care about the real address in snippets,
+ // So hardcode X1 for X-form Memory Operations for simplicity.
+ // TODO: materialize the offset into a reggister
+ setMemOp(IT, DispOpIdx, MCOperand::createReg(PPC::X1));
+ else
+ setMemOp(IT, DispOpIdx, MCOperand::createImm(Offset)); // Disp
+ setMemOp(IT, MemOpIdx + 2, MCOperand::createReg(Reg)); // BaseReg
+}
+
std::vector<MCInst> ExegesisPowerPCTarget::setRegTo(const MCSubtargetInfo &STI,
unsigned Reg,
const APInt &Value) const {
+ // X11 is optional use in function linkage, should be the least used one
+ // Use it as scratch reg to load immediate.
+ unsigned ScratchImmReg = PPC::X11;
+
if (PPC::GPRCRegClass.contains(Reg))
return {loadImmediate(Reg, 32, Value)};
if (PPC::G8RCRegClass.contains(Reg))
return {loadImmediate(Reg, 64, Value)};
- errs() << "setRegTo is not implemented, results will be unreliable\n";
+ if (PPC::F4RCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(ScratchImmReg)};
+ // We don't care the real value in reg, so set 64 bits or duplicate 64 bits
+ // for simplicity.
+ // TODO: update these if we need a accurate 128 values in registers.
+ if (PPC::VRRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVRD).addReg(Reg).addReg(ScratchImmReg)};
+ if (PPC::VSRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRDD)
+ .addReg(Reg)
+ .addReg(ScratchImmReg)
+ .addReg(ScratchImmReg)};
+ if (PPC::VFRCRegClass.contains(Reg))
+ return {loadImmediate(ScratchImmReg, 64, Value),
+ MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(ScratchImmReg)};
+ // SPE not supported yet
+ if (PPC::SPERCRegClass.contains(Reg)) {
+ errs() << "Unsupported SPE Reg:" << Reg << "\n";
+ return {};
+ }
+ errs() << "setRegTo is not implemented, results will be unreliable:" << Reg
+ << "\n";
return {};
}
add_llvm_target_unittest(LLVMExegesisPowerPCTests
AnalysisTest.cpp
+ SnippetGeneratorTest.cpp
TargetTest.cpp
)
target_link_libraries(LLVMExegesisPowerPCTests PRIVATE
--- /dev/null
+//===-- SnippetGeneratorTest.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 "../Common/AssemblerUtils.h"
+#include "LlvmState.h"
+#include "MCInstrDescView.h"
+#include "PPCInstrInfo.h"
+#include "ParallelSnippetGenerator.h"
+#include "RegisterAliasing.h"
+#include "SerialSnippetGenerator.h"
+#include "TestBase.h"
+
+#include <unordered_set>
+
+namespace llvm {
+namespace exegesis {
+namespace {
+
+using testing::AnyOf;
+using testing::ElementsAre;
+using testing::HasSubstr;
+using testing::SizeIs;
+
+MATCHER(IsInvalid, "") { return !arg.isValid(); }
+MATCHER(IsReg, "") { return arg.isReg(); }
+
+class PPCSnippetGeneratorTest : public PPCTestBase {};
+
+template <typename SnippetGeneratorT>
+class SnippetGeneratorTest : public PPCSnippetGeneratorTest {
+protected:
+ SnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()) {}
+
+ std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) {
+ randomGenerator().seed(0); // Initialize seed.
+ const Instruction &Instr = State.getIC().getInstr(Opcode);
+ auto CodeTemplateOrError = Generator.generateCodeTemplates(
+ &Instr, State.getRATC().emptyRegisters());
+ EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
+ return std::move(CodeTemplateOrError.get());
+ }
+
+ SnippetGeneratorT Generator;
+};
+
+using SerialSnippetGeneratorTest = SnippetGeneratorTest<SerialSnippetGenerator>;
+
+using ParallelSnippetGeneratorTest =
+ SnippetGeneratorTest<ParallelSnippetGenerator>;
+
+TEST_F(SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) {
+ // - ADD8
+ // - Op0 Explicit Def RegClass(G8RC)
+ // - Op1 Explicit Use RegClass(G8RC)
+ // - Op2 Explicit Use RegClass(G8RC)
+ // - Var0 [Op0]
+ // - Var1 [Op1]
+ // - Var2 [Op2]
+ // - hasAliasingRegisters
+ const unsigned Opcode = PPC::ADD8;
+ const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
+ ASSERT_THAT(CodeTemplates, SizeIs(1));
+ const auto &CT = CodeTemplates[0];
+ EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);
+ ASSERT_THAT(CT.Instructions, SizeIs(1));
+ const InstructionTemplate &IT = CT.Instructions[0];
+ EXPECT_THAT(IT.getOpcode(), Opcode);
+ ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
+ EXPECT_THAT(IT.getVariableValues(),
+ AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),
+ ElementsAre(IsReg(), IsReg(), IsInvalid())))
+ << "Op0 is either set to Op1 or to Op2";
+}
+
+TEST_F(SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughTiedRegs) {
+
+ // - RLDIMI
+ // - Op0 Explicit Def RegClass(G8RC)
+ // - Op1 Explicit Use RegClass(G8RC) TiedToOp0
+ // - Op2 Explicit Use RegClass(G8RC)
+ // - Op3 Explicit Use Immediate
+ // - Op4 Explicit Use Immediate
+ // - Var0 [Op0,Op1]
+ // - Var1 [Op2]
+ // - Var2 [Op3]
+ // - Var3 [Op4]
+ // - hasTiedRegisters (execution is always serial)
+ // - hasAliasingRegisters
+ // - RLDIMI
+ const unsigned Opcode = PPC::RLDIMI;
+ const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
+ ASSERT_THAT(CodeTemplates, SizeIs(1));
+ const auto &CT = CodeTemplates[0];
+ EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS);
+ ASSERT_THAT(CT.Instructions, SizeIs(1));
+ const InstructionTemplate &IT = CT.Instructions[0];
+ EXPECT_THAT(IT.getOpcode(), Opcode);
+ ASSERT_THAT(IT.getVariableValues(), SizeIs(4));
+ EXPECT_THAT(IT.getVariableValues()[2], IsInvalid()) << "Operand 1 is not set";
+ EXPECT_THAT(IT.getVariableValues()[3], IsInvalid()) << "Operand 2 is not set";
+}
+
+TEST_F(ParallelSnippetGeneratorTest, MemoryUse) {
+ // - LDX
+ // - Op0 Explicit Def RegClass(G8RC)
+ // - Op1 Explicit Use Memory RegClass(GPRC)
+ // - Op2 Explicit Use Memory RegClass(VSSRC)
+ // - Var0 [Op0]
+ // - Var1 [Op1]
+ // - Var2 [Op2]
+ // - hasMemoryOperands
+ // - hasAliasingRegisters
+ const unsigned Opcode = PPC::LDX;
+ const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
+ ASSERT_THAT(CodeTemplates, SizeIs(1));
+ const auto &CT = CodeTemplates[0];
+ EXPECT_THAT(CT.Info, HasSubstr("instruction has no tied variables picking "
+ "Uses different from defs"));
+ EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
+ ASSERT_THAT(CT.Instructions,
+ SizeIs(ParallelSnippetGenerator::kMinNumDifferentAddresses));
+ const InstructionTemplate &IT = CT.Instructions[0];
+ EXPECT_THAT(IT.getOpcode(), Opcode);
+ ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
+ EXPECT_EQ(IT.getVariableValues()[1].getReg(), PPC::X1);
+ EXPECT_EQ(IT.getVariableValues()[2].getReg(), PPC::X13);
+}
+
+} // namespace
+} // namespace exegesis
+} // namespace llvm
--- /dev/null
+//===-- TestBase.h ----------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+// Test fixture common to all PowerPC tests.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_POWERPC_TESTBASE_H
+#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_POWERPC_TESTBASE_H
+
+#include "LlvmState.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace exegesis {
+
+void InitializePowerPCExegesisTarget();
+
+class PPCTestBase : public ::testing::Test {
+protected:
+ PPCTestBase() : State("powerpc64le-unknown-linux", "ppc64le") {}
+
+ static void SetUpTestCase() {
+ LLVMInitializePowerPCTargetInfo();
+ LLVMInitializePowerPCTargetMC();
+ LLVMInitializePowerPCTarget();
+ InitializePowerPCExegesisTarget();
+ }
+
+ const LLVMState State;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif