123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- //===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- #include "../Common/AssemblerUtils.h"
- #include "Latency.h"
- #include "LlvmState.h"
- #include "MCInstrDescView.h"
- #include "RegisterAliasing.h"
- #include "Uops.h"
- #include "X86InstrInfo.h"
- #include <unordered_set>
- namespace exegesis {
- void InitializeX86ExegesisTarget();
- namespace {
- using testing::AnyOf;
- using testing::ElementsAre;
- using testing::HasSubstr;
- using testing::Not;
- using testing::SizeIs;
- using testing::UnorderedElementsAre;
- MATCHER(IsInvalid, "") { return !arg.isValid(); }
- MATCHER(IsReg, "") { return arg.isReg(); }
- class X86SnippetGeneratorTest : public ::testing::Test {
- protected:
- X86SnippetGeneratorTest()
- : State("x86_64-unknown-linux", "haswell"),
- MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
- static void SetUpTestCase() {
- LLVMInitializeX86TargetInfo();
- LLVMInitializeX86TargetMC();
- LLVMInitializeX86Target();
- LLVMInitializeX86AsmPrinter();
- InitializeX86ExegesisTarget();
- }
- const LLVMState State;
- const llvm::MCInstrInfo &MCInstrInfo;
- const llvm::MCRegisterInfo &MCRegisterInfo;
- };
- template <typename SnippetGeneratorT>
- class SnippetGeneratorTest : public X86SnippetGeneratorTest {
- protected:
- SnippetGeneratorTest() : Generator(State) {}
- CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) {
- randomGenerator().seed(0); // Initialize seed.
- const Instruction Instr(State, Opcode);
- auto CodeTemplateOrError = Generator.generateCodeTemplate(Instr);
- EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
- return std::move(CodeTemplateOrError.get());
- }
- SnippetGeneratorT Generator;
- };
- using LatencySnippetGeneratorTest =
- SnippetGeneratorTest<LatencySnippetGenerator>;
- using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsSnippetGenerator>;
- TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
- // ADC16i16 self alias because of implicit use and def.
- // explicit use 0 : imm
- // implicit def : AX
- // implicit def : EFLAGS
- // implicit use : AX
- // implicit use : EFLAGS
- const unsigned Opcode = llvm::X86::ADC16i16;
- EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
- EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
- EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
- EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("implicit"));
- ASSERT_THAT(CT.Instructions, SizeIs(1));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(1)); // Imm.
- EXPECT_THAT(IT.VariableValues[0], IsInvalid()) << "Immediate is not set";
- }
- TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
- // ADD16ri self alias because Op0 and Op1 are tied together.
- // explicit def 0 : reg RegClass=GR16
- // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
- // explicit use 2 : imm
- // implicit def : EFLAGS
- const unsigned Opcode = llvm::X86::ADD16ri;
- EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("explicit"));
- ASSERT_THAT(CT.Instructions, SizeIs(1));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(2));
- EXPECT_THAT(IT.VariableValues[0], IsReg()) << "Operand 0 and 1";
- EXPECT_THAT(IT.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
- }
- TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
- // CMP64rr
- // explicit use 0 : reg RegClass=GR64
- // explicit use 1 : reg RegClass=GR64
- // implicit def : EFLAGS
- const unsigned Opcode = llvm::X86::CMP64rr;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
- ASSERT_THAT(CT.Instructions, SizeIs(2));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(2));
- EXPECT_THAT(IT.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
- ElementsAre(IsInvalid(), IsReg())));
- EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode));
- // TODO: check that the two instructions alias each other.
- }
- TEST_F(LatencySnippetGeneratorTest, LAHF) {
- const unsigned Opcode = llvm::X86::LAHF;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
- ASSERT_THAT(CT.Instructions, SizeIs(2));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(0));
- }
- TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
- // BNDCL32rr is parallel no matter what.
- // explicit use 0 : reg RegClass=BNDR
- // explicit use 1 : reg RegClass=GR32
- const unsigned Opcode = llvm::X86::BNDCL32rr;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("parallel"));
- ASSERT_THAT(CT.Instructions, SizeIs(1));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(2));
- EXPECT_THAT(IT.VariableValues[0], IsInvalid());
- EXPECT_THAT(IT.VariableValues[1], IsInvalid());
- }
- TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
- // CDQ is serial no matter what.
- // implicit def : EAX
- // implicit def : EDX
- // implicit use : EAX
- const unsigned Opcode = llvm::X86::CDQ;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("serial"));
- ASSERT_THAT(CT.Instructions, SizeIs(1));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(0));
- }
- TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
- // CMOVA32rr has tied variables, we enumerate the possible values to execute
- // as many in parallel as possible.
- // explicit def 0 : reg RegClass=GR32
- // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
- // explicit use 2 : reg RegClass=GR32
- // implicit use : EFLAGS
- const unsigned Opcode = llvm::X86::CMOVA32rr;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("static renaming"));
- constexpr const unsigned kInstructionCount = 15;
- ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount));
- std::unordered_set<unsigned> AllDefRegisters;
- for (const auto &IT : CT.Instructions) {
- ASSERT_THAT(IT.VariableValues, SizeIs(2));
- AllDefRegisters.insert(IT.VariableValues[0].getReg());
- }
- EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
- << "Each instruction writes to a different register";
- }
- TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
- // CMOV_GR32 has no tied variables, we make sure def and use are different
- // from each other.
- // explicit def 0 : reg RegClass=GR32
- // explicit use 1 : reg RegClass=GR32
- // explicit use 2 : reg RegClass=GR32
- // explicit use 3 : imm
- // implicit use : EFLAGS
- const unsigned Opcode = llvm::X86::CMOV_GR32;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
- ASSERT_THAT(CT.Instructions, SizeIs(1));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(4));
- EXPECT_THAT(IT.VariableValues[0].getReg(), Not(IT.VariableValues[1].getReg()))
- << "Def is different from first Use";
- EXPECT_THAT(IT.VariableValues[0].getReg(), Not(IT.VariableValues[2].getReg()))
- << "Def is different from second Use";
- EXPECT_THAT(IT.VariableValues[3], IsInvalid());
- }
- TEST_F(UopsSnippetGeneratorTest, MemoryUse) {
- // Mov32rm reads from memory.
- const unsigned Opcode = llvm::X86::MOV32rm;
- const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
- EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
- ASSERT_THAT(CT.Instructions,
- SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses));
- const InstructionTemplate &IT = CT.Instructions[0];
- EXPECT_THAT(IT.getOpcode(), Opcode);
- ASSERT_THAT(IT.VariableValues, SizeIs(6));
- EXPECT_EQ(IT.VariableValues[2].getImm(), 1);
- EXPECT_EQ(IT.VariableValues[3].getReg(), 0u);
- EXPECT_EQ(IT.VariableValues[4].getImm(), 0);
- EXPECT_EQ(IT.VariableValues[5].getReg(), 0u);
- }
- TEST_F(UopsSnippetGeneratorTest, MemoryUse_Movsb) {
- // MOVSB writes to scratch memory register.
- const unsigned Opcode = llvm::X86::MOVSB;
- const Instruction Instr(State, Opcode);
- auto Error = Generator.generateCodeTemplate(Instr).takeError();
- EXPECT_TRUE((bool)Error);
- llvm::consumeError(std::move(Error));
- }
- class FakeSnippetGenerator : public SnippetGenerator {
- public:
- FakeSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
- Instruction createInstruction(unsigned Opcode) {
- return Instruction(State, Opcode);
- }
- private:
- llvm::Expected<CodeTemplate>
- generateCodeTemplate(const Instruction &Instr) const override {
- return llvm::make_error<llvm::StringError>("not implemented",
- llvm::inconvertibleErrorCode());
- }
- };
- using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeSnippetGenerator>;
- testing::Matcher<const RegisterValue &> IsRegisterValue(unsigned Reg,
- llvm::APInt Value) {
- return testing::AllOf(testing::Field(&RegisterValue::Register, Reg),
- testing::Field(&RegisterValue::Value, Value));
- }
- TEST_F(FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd16ri) {
- // ADD16ri:
- // explicit def 0 : reg RegClass=GR16
- // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
- // explicit use 2 : imm
- // implicit def : EFLAGS
- InstructionTemplate IT(Generator.createInstruction(llvm::X86::ADD16ri));
- IT.getValueFor(IT.Instr.Variables[0]) =
- llvm::MCOperand::createReg(llvm::X86::AX);
- std::vector<InstructionTemplate> Snippet;
- Snippet.push_back(std::move(IT));
- const auto RIV = Generator.computeRegisterInitialValues(Snippet);
- EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::AX, llvm::APInt())));
- }
- TEST_F(FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd64rr) {
- // ADD64rr:
- // mov64ri rax, 42
- // add64rr rax, rax, rbx
- // -> only rbx needs defining.
- std::vector<InstructionTemplate> Snippet;
- {
- InstructionTemplate Mov(Generator.createInstruction(llvm::X86::MOV64ri));
- Mov.getValueFor(Mov.Instr.Variables[0]) =
- llvm::MCOperand::createReg(llvm::X86::RAX);
- Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
- Snippet.push_back(std::move(Mov));
- }
- {
- InstructionTemplate Add(Generator.createInstruction(llvm::X86::ADD64rr));
- Add.getValueFor(Add.Instr.Variables[0]) =
- llvm::MCOperand::createReg(llvm::X86::RAX);
- Add.getValueFor(Add.Instr.Variables[1]) =
- llvm::MCOperand::createReg(llvm::X86::RBX);
- Snippet.push_back(std::move(Add));
- }
- const auto RIV = Generator.computeRegisterInitialValues(Snippet);
- EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::RBX, llvm::APInt())));
- }
- } // namespace
- } // namespace exegesis
|