1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

[llvm-exegesis][PowerPC] Add more register classes

This PR adds more register class support in PowerPC,
mark OperandType for imm and memory operands.

Also added more unit tests for SnippetGenerator.

Reviewed By: #powerpc, steven.zhang

Differential Revision: https://reviews.llvm.org/D88044
This commit is contained in:
Jinsong Ji 2020-12-04 14:07:52 +00:00
parent 65bd36f2bc
commit a97bf3c2f2
6 changed files with 276 additions and 1 deletions

View File

@ -19,12 +19,14 @@ def s16imm64 : Operand<i64> {
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
@ -34,6 +36,7 @@ def s17imm64 : Operand<i64> {
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS17ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
let OperandType = "OPERAND_IMMEDIATE";
}
def tocentry : Operand<iPTR> {
let MIOperandInfo = (ops i64imm:$imm);

View File

@ -666,6 +666,7 @@ def PPCU1ImmAsmOperand : AsmOperandClass {
def u1imm : Operand<i32> {
let PrintMethod = "printU1ImmOperand";
let ParserMatchClass = PPCU1ImmAsmOperand;
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU2ImmAsmOperand : AsmOperandClass {
@ -675,6 +676,7 @@ def PPCU2ImmAsmOperand : AsmOperandClass {
def u2imm : Operand<i32> {
let PrintMethod = "printU2ImmOperand";
let ParserMatchClass = PPCU2ImmAsmOperand;
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCATBitsAsHintAsmOperand : AsmOperandClass {
@ -684,6 +686,7 @@ def PPCATBitsAsHintAsmOperand : AsmOperandClass {
def atimm : Operand<i32> {
let PrintMethod = "printATBitsAsHint";
let ParserMatchClass = PPCATBitsAsHintAsmOperand;
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU3ImmAsmOperand : AsmOperandClass {
@ -693,6 +696,7 @@ def PPCU3ImmAsmOperand : AsmOperandClass {
def u3imm : Operand<i32> {
let PrintMethod = "printU3ImmOperand";
let ParserMatchClass = PPCU3ImmAsmOperand;
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU4ImmAsmOperand : AsmOperandClass {
@ -702,6 +706,7 @@ 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";
@ -711,6 +716,7 @@ def s5imm : Operand<i32> {
let PrintMethod = "printS5ImmOperand";
let ParserMatchClass = PPCS5ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<5>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU5ImmAsmOperand : AsmOperandClass {
let Name = "U5Imm"; let PredicateMethod = "isU5Imm";
@ -720,6 +726,7 @@ def u5imm : Operand<i32> {
let PrintMethod = "printU5ImmOperand";
let ParserMatchClass = PPCU5ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<5>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU6ImmAsmOperand : AsmOperandClass {
let Name = "U6Imm"; let PredicateMethod = "isU6Imm";
@ -729,6 +736,7 @@ def u6imm : Operand<i32> {
let PrintMethod = "printU6ImmOperand";
let ParserMatchClass = PPCU6ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<6>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU7ImmAsmOperand : AsmOperandClass {
let Name = "U7Imm"; let PredicateMethod = "isU7Imm";
@ -738,6 +746,7 @@ def u7imm : Operand<i32> {
let PrintMethod = "printU7ImmOperand";
let ParserMatchClass = PPCU7ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<7>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU8ImmAsmOperand : AsmOperandClass {
let Name = "U8Imm"; let PredicateMethod = "isU8Imm";
@ -747,6 +756,7 @@ def u8imm : Operand<i32> {
let PrintMethod = "printU8ImmOperand";
let ParserMatchClass = PPCU8ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<8>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU10ImmAsmOperand : AsmOperandClass {
let Name = "U10Imm"; let PredicateMethod = "isU10Imm";
@ -756,6 +766,7 @@ def u10imm : Operand<i32> {
let PrintMethod = "printU10ImmOperand";
let ParserMatchClass = PPCU10ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<10>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU12ImmAsmOperand : AsmOperandClass {
let Name = "U12Imm"; let PredicateMethod = "isU12Imm";
@ -765,6 +776,7 @@ def u12imm : Operand<i32> {
let PrintMethod = "printU12ImmOperand";
let ParserMatchClass = PPCU12ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<12>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS16ImmAsmOperand : AsmOperandClass {
let Name = "S16Imm"; let PredicateMethod = "isS16Imm";
@ -775,6 +787,7 @@ def s16imm : Operand<i32> {
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS16ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCU16ImmAsmOperand : AsmOperandClass {
let Name = "U16Imm"; let PredicateMethod = "isU16Imm";
@ -785,6 +798,7 @@ def u16imm : Operand<i32> {
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCU16ImmAsmOperand;
let DecoderMethod = "decodeUImmOperand<16>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS17ImmAsmOperand : AsmOperandClass {
let Name = "S17Imm"; let PredicateMethod = "isS17Imm";
@ -798,6 +812,7 @@ def s17imm : Operand<i32> {
let EncoderMethod = "getImm16Encoding";
let ParserMatchClass = PPCS17ImmAsmOperand;
let DecoderMethod = "decodeSImmOperand<16>";
let OperandType = "OPERAND_IMMEDIATE";
}
def PPCS34ImmAsmOperand : AsmOperandClass {
let Name = "S34Imm";
@ -809,12 +824,14 @@ def s34imm : Operand<i64> {
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";
@ -825,6 +842,7 @@ def immZero : Operand<i32> {
let PrintMethod = "printImmZeroOperand";
let ParserMatchClass = PPCImmZeroAsmOperand;
let DecoderMethod = "decodeImmZeroOperand";
let OperandType = "OPERAND_IMMEDIATE";
}
def fpimm0 : PatLeaf<(fpimm), [{ return N->isExactlyValue(+0.0); }]>;
@ -970,40 +988,47 @@ def memri : Operand<iPTR> {
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
@ -1011,6 +1036,7 @@ def spe2dis : Operand<iPTR> { // SPE displacement where the imm is 2-aligned.
// 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";

View File

@ -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 @@ private:
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
@ -44,19 +55,75 @@ static MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth,
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 {};
}

View File

@ -15,6 +15,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_target_unittest(LLVMExegesisPowerPCTests
AnalysisTest.cpp
SnippetGeneratorTest.cpp
TargetTest.cpp
)
target_link_libraries(LLVMExegesisPowerPCTests PRIVATE

View File

@ -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 <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

View File

@ -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_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