blob: 6e2810bfee1b1350c08e2e4f62caf5216c2bdd9b [file] [log] [blame]
Guillaume Chateletc9f727b2018-06-13 13:24:41 +00001//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "../Common/AssemblerUtils.h"
11#include "Latency.h"
12#include "LlvmState.h"
13#include "MCInstrDescView.h"
14#include "RegisterAliasing.h"
15#include "Uops.h"
16#include "X86InstrInfo.h"
17
18#include <unordered_set>
19
20namespace exegesis {
Guillaume Chateletfb943542018-08-01 14:41:45 +000021
22void InitializeX86ExegesisTarget();
23
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000024namespace {
25
Guillaume Chatelet1ebb6752018-06-20 11:09:36 +000026using testing::AnyOf;
27using testing::ElementsAre;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000028using testing::HasSubstr;
29using testing::Not;
30using testing::SizeIs;
Clement Courbeta51efc22018-06-25 13:12:02 +000031using testing::UnorderedElementsAre;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000032
33MATCHER(IsInvalid, "") { return !arg.isValid(); }
34MATCHER(IsReg, "") { return arg.isReg(); }
35
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000036class X86SnippetGeneratorTest : public ::testing::Test {
37protected:
38 X86SnippetGeneratorTest()
Guillaume Chateletb391f242018-06-13 14:07:36 +000039 : State("x86_64-unknown-linux", "haswell"),
40 MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000041
42 static void SetUpTestCase() {
43 LLVMInitializeX86TargetInfo();
44 LLVMInitializeX86TargetMC();
45 LLVMInitializeX86Target();
46 LLVMInitializeX86AsmPrinter();
Guillaume Chateletfb943542018-08-01 14:41:45 +000047 InitializeX86ExegesisTarget();
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000048 }
49
50 const LLVMState State;
51 const llvm::MCInstrInfo &MCInstrInfo;
52 const llvm::MCRegisterInfo &MCRegisterInfo;
53};
54
Guillaume Chateletef6cef52018-06-20 08:52:30 +000055template <typename BenchmarkRunner>
56class SnippetGeneratorTest : public X86SnippetGeneratorTest {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000057protected:
Guillaume Chateletef6cef52018-06-20 08:52:30 +000058 SnippetGeneratorTest() : Runner(State) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000059
Guillaume Chatelete60866a2018-08-03 09:29:38 +000060 CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000061 randomGenerator().seed(0); // Initialize seed.
Guillaume Chatelete60866a2018-08-03 09:29:38 +000062 auto CodeTemplateOrError = Runner.generateCodeTemplate(Opcode);
63 EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
64 return std::move(CodeTemplateOrError.get());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000065 }
66
Guillaume Chateletef6cef52018-06-20 08:52:30 +000067 BenchmarkRunner Runner;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000068};
69
Guillaume Chatelete60866a2018-08-03 09:29:38 +000070using LatencyBenchmarkRunnerTest = SnippetGeneratorTest<LatencyBenchmarkRunner>;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000071
Guillaume Chatelete60866a2018-08-03 09:29:38 +000072using UopsBenchmarkRunnerTest = SnippetGeneratorTest<UopsBenchmarkRunner>;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000073
Guillaume Chatelete60866a2018-08-03 09:29:38 +000074TEST_F(LatencyBenchmarkRunnerTest, ImplicitSelfDependency) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000075 // ADC16i16 self alias because of implicit use and def.
76
77 // explicit use 0 : imm
78 // implicit def : AX
79 // implicit def : EFLAGS
80 // implicit use : AX
81 // implicit use : EFLAGS
82 const unsigned Opcode = llvm::X86::ADC16i16;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000083 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
84 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
85 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
86 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
Guillaume Chatelete60866a2018-08-03 09:29:38 +000087 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
88 EXPECT_THAT(CT.Info, HasSubstr("implicit"));
89 ASSERT_THAT(CT.Instructions, SizeIs(1));
90 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +000091 EXPECT_THAT(IB.getOpcode(), Opcode);
92 ASSERT_THAT(IB.VariableValues, SizeIs(1)); // Imm.
93 EXPECT_THAT(IB.VariableValues[0], IsInvalid()) << "Immediate is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000094}
95
Guillaume Chatelete60866a2018-08-03 09:29:38 +000096TEST_F(LatencyBenchmarkRunnerTest, ExplicitSelfDependency) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000097 // ADD16ri self alias because Op0 and Op1 are tied together.
98
99 // explicit def 0 : reg RegClass=GR16
100 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
101 // explicit use 2 : imm
102 // implicit def : EFLAGS
103 const unsigned Opcode = llvm::X86::ADD16ri;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000104 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000105 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
106 EXPECT_THAT(CT.Info, HasSubstr("explicit"));
107 ASSERT_THAT(CT.Instructions, SizeIs(1));
108 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000109 EXPECT_THAT(IB.getOpcode(), Opcode);
110 ASSERT_THAT(IB.VariableValues, SizeIs(2));
111 EXPECT_THAT(IB.VariableValues[0], IsReg()) << "Operand 0 and 1";
112 EXPECT_THAT(IB.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000113}
114
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000115TEST_F(LatencyBenchmarkRunnerTest, DependencyThroughOtherOpcode) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000116 // CMP64rr
117 // explicit use 0 : reg RegClass=GR64
118 // explicit use 1 : reg RegClass=GR64
119 // implicit def : EFLAGS
120
121 const unsigned Opcode = llvm::X86::CMP64rr;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000122 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
123 EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
124 ASSERT_THAT(CT.Instructions, SizeIs(2));
125 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000126 EXPECT_THAT(IB.getOpcode(), Opcode);
127 ASSERT_THAT(IB.VariableValues, SizeIs(2));
128 EXPECT_THAT(IB.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
Guillaume Chatelet1ebb6752018-06-20 11:09:36 +0000129 ElementsAre(IsInvalid(), IsReg())));
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000130 EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000131 // TODO: check that the two instructions alias each other.
132}
133
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000134TEST_F(LatencyBenchmarkRunnerTest, LAHF) {
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000135 const unsigned Opcode = llvm::X86::LAHF;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000136 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
137 EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
138 ASSERT_THAT(CT.Instructions, SizeIs(2));
139 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000140 EXPECT_THAT(IB.getOpcode(), Opcode);
141 ASSERT_THAT(IB.VariableValues, SizeIs(0));
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000142}
143
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000144TEST_F(UopsBenchmarkRunnerTest, ParallelInstruction) {
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000145 // BNDCL32rr is parallel no matter what.
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000146
147 // explicit use 0 : reg RegClass=BNDR
148 // explicit use 1 : reg RegClass=GR32
149
150 const unsigned Opcode = llvm::X86::BNDCL32rr;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000151 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
152 EXPECT_THAT(CT.Info, HasSubstr("parallel"));
153 ASSERT_THAT(CT.Instructions, SizeIs(1));
154 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000155 EXPECT_THAT(IB.getOpcode(), Opcode);
156 ASSERT_THAT(IB.VariableValues, SizeIs(2));
157 EXPECT_THAT(IB.VariableValues[0], IsInvalid());
158 EXPECT_THAT(IB.VariableValues[1], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000159}
160
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000161TEST_F(UopsBenchmarkRunnerTest, SerialInstruction) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000162 // CDQ is serial no matter what.
163
164 // implicit def : EAX
165 // implicit def : EDX
166 // implicit use : EAX
167 const unsigned Opcode = llvm::X86::CDQ;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000168 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
169 EXPECT_THAT(CT.Info, HasSubstr("serial"));
170 ASSERT_THAT(CT.Instructions, SizeIs(1));
171 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000172 EXPECT_THAT(IB.getOpcode(), Opcode);
173 ASSERT_THAT(IB.VariableValues, SizeIs(0));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000174}
175
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000176TEST_F(UopsBenchmarkRunnerTest, StaticRenaming) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000177 // CMOVA32rr has tied variables, we enumarate the possible values to execute
178 // as many in parallel as possible.
179
180 // explicit def 0 : reg RegClass=GR32
181 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
182 // explicit use 2 : reg RegClass=GR32
183 // implicit use : EFLAGS
184 const unsigned Opcode = llvm::X86::CMOVA32rr;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000185 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
186 EXPECT_THAT(CT.Info, HasSubstr("static renaming"));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000187 constexpr const unsigned kInstructionCount = 15;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000188 ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000189 std::unordered_set<unsigned> AllDefRegisters;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000190 for (const auto &IB : CT.Instructions) {
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000191 ASSERT_THAT(IB.VariableValues, SizeIs(2));
192 AllDefRegisters.insert(IB.VariableValues[0].getReg());
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000193 }
194 EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000195 << "Each instruction writes to a different register";
196}
197
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000198TEST_F(UopsBenchmarkRunnerTest, NoTiedVariables) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000199 // CMOV_GR32 has no tied variables, we make sure def and use are different
200 // from each other.
201
202 // explicit def 0 : reg RegClass=GR32
203 // explicit use 1 : reg RegClass=GR32
204 // explicit use 2 : reg RegClass=GR32
205 // explicit use 3 : imm
206 // implicit use : EFLAGS
207 const unsigned Opcode = llvm::X86::CMOV_GR32;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000208 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
209 EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
210 ASSERT_THAT(CT.Instructions, SizeIs(1));
211 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000212 EXPECT_THAT(IB.getOpcode(), Opcode);
213 ASSERT_THAT(IB.VariableValues, SizeIs(4));
214 EXPECT_THAT(IB.VariableValues[0].getReg(), Not(IB.VariableValues[1].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000215 << "Def is different from first Use";
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000216 EXPECT_THAT(IB.VariableValues[0].getReg(), Not(IB.VariableValues[2].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000217 << "Def is different from second Use";
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000218 EXPECT_THAT(IB.VariableValues[3], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000219}
220
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000221TEST_F(UopsBenchmarkRunnerTest, MemoryUse) {
Guillaume Chateletfb943542018-08-01 14:41:45 +0000222 // Mov32rm reads from memory.
223 const unsigned Opcode = llvm::X86::MOV32rm;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000224 const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
225 EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
226 ASSERT_THAT(CT.Instructions,
Guillaume Chateletfb943542018-08-01 14:41:45 +0000227 SizeIs(UopsBenchmarkRunner::kMinNumDifferentAddresses));
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000228 const InstructionBuilder &IB = CT.Instructions[0];
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000229 EXPECT_THAT(IB.getOpcode(), Opcode);
230 ASSERT_THAT(IB.VariableValues, SizeIs(6));
231 EXPECT_EQ(IB.VariableValues[2].getImm(), 1);
232 EXPECT_EQ(IB.VariableValues[3].getReg(), 0u);
233 EXPECT_EQ(IB.VariableValues[4].getImm(), 0);
234 EXPECT_EQ(IB.VariableValues[5].getReg(), 0u);
Guillaume Chateletfb943542018-08-01 14:41:45 +0000235}
236
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000237TEST_F(UopsBenchmarkRunnerTest, MemoryUse_Movsb) {
Guillaume Chateletfb943542018-08-01 14:41:45 +0000238 // MOVSB writes to scratch memory register.
239 const unsigned Opcode = llvm::X86::MOVSB;
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000240 auto Error = Runner.generateCodeTemplate(Opcode).takeError();
Guillaume Chateletfb943542018-08-01 14:41:45 +0000241 EXPECT_TRUE((bool)Error);
242 llvm::consumeError(std::move(Error));
243}
244
Clement Courbeta51efc22018-06-25 13:12:02 +0000245class FakeBenchmarkRunner : public BenchmarkRunner {
246public:
Clement Courbet4860b982018-06-26 08:49:30 +0000247 FakeBenchmarkRunner(const LLVMState &State)
248 : BenchmarkRunner(State, InstructionBenchmark::Unknown) {}
Clement Courbeta51efc22018-06-25 13:12:02 +0000249
250 Instruction createInstruction(unsigned Opcode) {
Clement Courbet0e8bf4e2018-06-25 13:44:27 +0000251 return Instruction(State.getInstrInfo().get(Opcode), RATC);
Clement Courbeta51efc22018-06-25 13:12:02 +0000252 }
253
254private:
Guillaume Chatelete60866a2018-08-03 09:29:38 +0000255 llvm::Expected<CodeTemplate>
256 generateCodeTemplate(unsigned Opcode) const override {
Clement Courbeta51efc22018-06-25 13:12:02 +0000257 return llvm::make_error<llvm::StringError>("not implemented",
258 llvm::inconvertibleErrorCode());
259 }
260
261 std::vector<BenchmarkMeasure>
Guillaume Chateletfb943542018-08-01 14:41:45 +0000262 runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
Clement Courbeta51efc22018-06-25 13:12:02 +0000263 const unsigned NumRepetitions) const override {
264 return {};
265 }
266};
267
268using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeBenchmarkRunner>;
269
270TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) {
271 // ADD16ri:
272 // explicit def 0 : reg RegClass=GR16
273 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
274 // explicit use 2 : imm
275 // implicit def : EFLAGS
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000276 InstructionBuilder IB(Runner.createInstruction(llvm::X86::ADD16ri));
277 IB.getValueFor(IB.Instr.Variables[0]) =
Clement Courbeta51efc22018-06-25 13:12:02 +0000278 llvm::MCOperand::createReg(llvm::X86::AX);
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000279 std::vector<InstructionBuilder> Snippet;
280 Snippet.push_back(std::move(IB));
Clement Courbeta51efc22018-06-25 13:12:02 +0000281 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
282 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX));
283}
284
285TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) {
286 // ADD64rr:
287 // mov64ri rax, 42
288 // add64rr rax, rax, rbx
289 // -> only rbx needs defining.
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000290 std::vector<InstructionBuilder> Snippet;
Clement Courbeta51efc22018-06-25 13:12:02 +0000291 {
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000292 InstructionBuilder Mov(Runner.createInstruction(llvm::X86::MOV64ri));
Clement Courbeta51efc22018-06-25 13:12:02 +0000293 Mov.getValueFor(Mov.Instr.Variables[0]) =
294 llvm::MCOperand::createReg(llvm::X86::RAX);
295 Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
296 Snippet.push_back(std::move(Mov));
297 }
298 {
Guillaume Chatelet171f3f42018-08-02 11:12:02 +0000299 InstructionBuilder Add(Runner.createInstruction(llvm::X86::ADD64rr));
Clement Courbeta51efc22018-06-25 13:12:02 +0000300 Add.getValueFor(Add.Instr.Variables[0]) =
301 llvm::MCOperand::createReg(llvm::X86::RAX);
302 Add.getValueFor(Add.Instr.Variables[1]) =
303 llvm::MCOperand::createReg(llvm::X86::RBX);
304 Snippet.push_back(std::move(Add));
305 }
306
307 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
308 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX));
309}
310
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000311} // namespace
312} // namespace exegesis