diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -19,12 +19,14 @@ let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCS16ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def u16imm64 : Operand { let PrintMethod = "printU16ImmOperand"; let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCU16ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def s17imm64 : Operand { // This operand type is used for addis/lis to allow the assembler parser @@ -34,6 +36,7 @@ let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCS17ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def tocentry : Operand { let MIOperandInfo = (ops i64imm:$imm); diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -660,6 +660,7 @@ def u1imm : Operand { let PrintMethod = "printU1ImmOperand"; let ParserMatchClass = PPCU1ImmAsmOperand; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU2ImmAsmOperand : AsmOperandClass { @@ -669,6 +670,7 @@ def u2imm : Operand { let PrintMethod = "printU2ImmOperand"; let ParserMatchClass = PPCU2ImmAsmOperand; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCATBitsAsHintAsmOperand : AsmOperandClass { @@ -678,6 +680,7 @@ def atimm : Operand { let PrintMethod = "printATBitsAsHint"; let ParserMatchClass = PPCATBitsAsHintAsmOperand; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU3ImmAsmOperand : AsmOperandClass { @@ -687,6 +690,7 @@ def u3imm : Operand { let PrintMethod = "printU3ImmOperand"; let ParserMatchClass = PPCU3ImmAsmOperand; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU4ImmAsmOperand : AsmOperandClass { @@ -696,6 +700,7 @@ def u4imm : Operand { let PrintMethod = "printU4ImmOperand"; let ParserMatchClass = PPCU4ImmAsmOperand; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCS5ImmAsmOperand : AsmOperandClass { let Name = "S5Imm"; let PredicateMethod = "isS5Imm"; @@ -705,6 +710,7 @@ let PrintMethod = "printS5ImmOperand"; let ParserMatchClass = PPCS5ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<5>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU5ImmAsmOperand : AsmOperandClass { let Name = "U5Imm"; let PredicateMethod = "isU5Imm"; @@ -714,6 +720,7 @@ let PrintMethod = "printU5ImmOperand"; let ParserMatchClass = PPCU5ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<5>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU6ImmAsmOperand : AsmOperandClass { let Name = "U6Imm"; let PredicateMethod = "isU6Imm"; @@ -723,6 +730,7 @@ let PrintMethod = "printU6ImmOperand"; let ParserMatchClass = PPCU6ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<6>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU7ImmAsmOperand : AsmOperandClass { let Name = "U7Imm"; let PredicateMethod = "isU7Imm"; @@ -732,6 +740,7 @@ let PrintMethod = "printU7ImmOperand"; let ParserMatchClass = PPCU7ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<7>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU8ImmAsmOperand : AsmOperandClass { let Name = "U8Imm"; let PredicateMethod = "isU8Imm"; @@ -741,6 +750,7 @@ let PrintMethod = "printU8ImmOperand"; let ParserMatchClass = PPCU8ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<8>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU10ImmAsmOperand : AsmOperandClass { let Name = "U10Imm"; let PredicateMethod = "isU10Imm"; @@ -750,6 +760,7 @@ let PrintMethod = "printU10ImmOperand"; let ParserMatchClass = PPCU10ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<10>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU12ImmAsmOperand : AsmOperandClass { let Name = "U12Imm"; let PredicateMethod = "isU12Imm"; @@ -759,6 +770,7 @@ let PrintMethod = "printU12ImmOperand"; let ParserMatchClass = PPCU12ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<12>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCS16ImmAsmOperand : AsmOperandClass { let Name = "S16Imm"; let PredicateMethod = "isS16Imm"; @@ -769,6 +781,7 @@ let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCS16ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCU16ImmAsmOperand : AsmOperandClass { let Name = "U16Imm"; let PredicateMethod = "isU16Imm"; @@ -779,6 +792,7 @@ let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCU16ImmAsmOperand; let DecoderMethod = "decodeUImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCS17ImmAsmOperand : AsmOperandClass { let Name = "S17Imm"; let PredicateMethod = "isS17Imm"; @@ -792,6 +806,7 @@ let EncoderMethod = "getImm16Encoding"; let ParserMatchClass = PPCS17ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<16>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCS34ImmAsmOperand : AsmOperandClass { let Name = "S34Imm"; @@ -803,12 +818,14 @@ let EncoderMethod = "getImm34EncodingNoPCRel"; let ParserMatchClass = PPCS34ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<34>"; + let OperandType = "OPERAND_IMMEDIATE"; } def s34imm_pcrel : Operand { let PrintMethod = "printS34ImmOperand"; let EncoderMethod = "getImm34EncodingPCRel"; let ParserMatchClass = PPCS34ImmAsmOperand; let DecoderMethod = "decodeSImmOperand<34>"; + let OperandType = "OPERAND_IMMEDIATE"; } def PPCImmZeroAsmOperand : AsmOperandClass { let Name = "ImmZero"; @@ -819,6 +836,7 @@ let PrintMethod = "printImmZeroOperand"; let ParserMatchClass = PPCImmZeroAsmOperand; let DecoderMethod = "decodeImmZeroOperand"; + let OperandType = "OPERAND_IMMEDIATE"; } def fpimm0 : PatLeaf<(fpimm), [{ return N->isExactlyValue(+0.0); }]>; @@ -964,40 +982,47 @@ let MIOperandInfo = (ops dispRI:$imm, ptr_rc_nor0:$reg); let EncoderMethod = "getMemRIEncoding"; let DecoderMethod = "decodeMemRIOperands"; + let OperandType = "OPERAND_MEMORY"; } def memrr : Operand { let PrintMethod = "printMemRegReg"; let MIOperandInfo = (ops ptr_rc_nor0:$ptrreg, ptr_rc_idx:$offreg); + let OperandType = "OPERAND_MEMORY"; } def memrix : Operand { // 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 { // 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 { // 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 { // 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 { // 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 @@ -1005,6 +1030,7 @@ // G8RC_NOX0 registers. def memr : Operand { let MIOperandInfo = (ops ptr_rc_nor0:$ptrreg); + let OperandType = "OPERAND_MEMORY"; } def PPCTLSRegOperand : AsmOperandClass { let Name = "TLSReg"; let PredicateMethod = "isTLSReg"; diff --git a/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp b/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp --- a/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp @@ -13,6 +13,14 @@ 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 { @@ -26,6 +34,9 @@ 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 @@ -49,6 +60,29 @@ .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()) + // Hardcoded X1 for X-form Memory Operations + 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 ExegesisPowerPCTarget::setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const { @@ -56,7 +90,27 @@ 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(PPC::X11, 64, Value), + MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(PPC::X11)}; + if (PPC::VRRCRegClass.contains(Reg)) + return {loadImmediate(PPC::X11, 64, Value), + MCInstBuilder(PPC::MTVRD).addReg(Reg).addReg(PPC::X11)}; + if (PPC::VSRCRegClass.contains(Reg)) + return {loadImmediate(PPC::X11, 64, Value), MCInstBuilder(PPC::MTVSRDD) + .addReg(Reg) + .addReg(PPC::X11) + .addReg(PPC::X11)}; + if (PPC::VFRCRegClass.contains(Reg)) + return {loadImmediate(PPC::X11, 64, Value), + MCInstBuilder(PPC::MTVSRD).addReg(Reg).addReg(PPC::X11)}; + // 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 {}; } diff --git a/llvm/unittests/tools/llvm-exegesis/PowerPC/CMakeLists.txt b/llvm/unittests/tools/llvm-exegesis/PowerPC/CMakeLists.txt --- a/llvm/unittests/tools/llvm-exegesis/PowerPC/CMakeLists.txt +++ b/llvm/unittests/tools/llvm-exegesis/PowerPC/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_target_unittest(LLVMExegesisPowerPCTests AnalysisTest.cpp + SnippetGeneratorTest.cpp TargetTest.cpp ) target_link_libraries(LLVMExegesisPowerPCTests PRIVATE diff --git a/llvm/unittests/tools/llvm-exegesis/PowerPC/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/PowerPC/SnippetGeneratorTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-exegesis/PowerPC/SnippetGeneratorTest.cpp @@ -0,0 +1,136 @@ +//===-- 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 + +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 +class SnippetGeneratorTest : public PPCSnippetGeneratorTest { +protected: + SnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()) {} + + std::vector 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; + +using ParallelSnippetGeneratorTest = + SnippetGeneratorTest; + +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 diff --git a/llvm/unittests/tools/llvm-exegesis/PowerPC/TestBase.h b/llvm/unittests/tools/llvm-exegesis/PowerPC/TestBase.h new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-exegesis/PowerPC/TestBase.h @@ -0,0 +1,42 @@ +//===-- 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_MIPS_TESTBASE_H +#define LLVM_UNITTESTS_TOOLS_LLVMEXEGESIS_MIPS_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