SnippetGeneratorTest.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. //===-- 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. #include "../Common/AssemblerUtils.h"
  10. #include "Latency.h"
  11. #include "LlvmState.h"
  12. #include "MCInstrDescView.h"
  13. #include "RegisterAliasing.h"
  14. #include "Uops.h"
  15. #include "X86InstrInfo.h"
  16. #include <unordered_set>
  17. namespace exegesis {
  18. void InitializeX86ExegesisTarget();
  19. namespace {
  20. using testing::AnyOf;
  21. using testing::ElementsAre;
  22. using testing::HasSubstr;
  23. using testing::Not;
  24. using testing::SizeIs;
  25. using testing::UnorderedElementsAre;
  26. MATCHER(IsInvalid, "") { return !arg.isValid(); }
  27. MATCHER(IsReg, "") { return arg.isReg(); }
  28. class X86SnippetGeneratorTest : public ::testing::Test {
  29. protected:
  30. X86SnippetGeneratorTest()
  31. : State("x86_64-unknown-linux", "haswell"),
  32. MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
  33. static void SetUpTestCase() {
  34. LLVMInitializeX86TargetInfo();
  35. LLVMInitializeX86TargetMC();
  36. LLVMInitializeX86Target();
  37. LLVMInitializeX86AsmPrinter();
  38. InitializeX86ExegesisTarget();
  39. }
  40. const LLVMState State;
  41. const llvm::MCInstrInfo &MCInstrInfo;
  42. const llvm::MCRegisterInfo &MCRegisterInfo;
  43. };
  44. template <typename SnippetGeneratorT>
  45. class SnippetGeneratorTest : public X86SnippetGeneratorTest {
  46. protected:
  47. SnippetGeneratorTest() : Generator(State) {}
  48. CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) {
  49. randomGenerator().seed(0); // Initialize seed.
  50. const Instruction Instr(State, Opcode);
  51. auto CodeTemplateOrError = Generator.generateCodeTemplate(Instr);
  52. EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
  53. return std::move(CodeTemplateOrError.get());
  54. }
  55. SnippetGeneratorT Generator;
  56. };
  57. using LatencySnippetGeneratorTest =
  58. SnippetGeneratorTest<LatencySnippetGenerator>;
  59. using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsSnippetGenerator>;
  60. TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
  61. // ADC16i16 self alias because of implicit use and def.
  62. // explicit use 0 : imm
  63. // implicit def : AX
  64. // implicit def : EFLAGS
  65. // implicit use : AX
  66. // implicit use : EFLAGS
  67. const unsigned Opcode = llvm::X86::ADC16i16;
  68. EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
  69. EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
  70. EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
  71. EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
  72. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  73. EXPECT_THAT(CT.Info, HasSubstr("implicit"));
  74. ASSERT_THAT(CT.Instructions, SizeIs(1));
  75. const InstructionTemplate &IT = CT.Instructions[0];
  76. EXPECT_THAT(IT.getOpcode(), Opcode);
  77. ASSERT_THAT(IT.VariableValues, SizeIs(1)); // Imm.
  78. EXPECT_THAT(IT.VariableValues[0], IsInvalid()) << "Immediate is not set";
  79. }
  80. TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
  81. // ADD16ri self alias because Op0 and Op1 are tied together.
  82. // explicit def 0 : reg RegClass=GR16
  83. // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
  84. // explicit use 2 : imm
  85. // implicit def : EFLAGS
  86. const unsigned Opcode = llvm::X86::ADD16ri;
  87. EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
  88. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  89. EXPECT_THAT(CT.Info, HasSubstr("explicit"));
  90. ASSERT_THAT(CT.Instructions, SizeIs(1));
  91. const InstructionTemplate &IT = CT.Instructions[0];
  92. EXPECT_THAT(IT.getOpcode(), Opcode);
  93. ASSERT_THAT(IT.VariableValues, SizeIs(2));
  94. EXPECT_THAT(IT.VariableValues[0], IsReg()) << "Operand 0 and 1";
  95. EXPECT_THAT(IT.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
  96. }
  97. TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
  98. // CMP64rr
  99. // explicit use 0 : reg RegClass=GR64
  100. // explicit use 1 : reg RegClass=GR64
  101. // implicit def : EFLAGS
  102. const unsigned Opcode = llvm::X86::CMP64rr;
  103. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  104. EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
  105. ASSERT_THAT(CT.Instructions, SizeIs(2));
  106. const InstructionTemplate &IT = CT.Instructions[0];
  107. EXPECT_THAT(IT.getOpcode(), Opcode);
  108. ASSERT_THAT(IT.VariableValues, SizeIs(2));
  109. EXPECT_THAT(IT.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
  110. ElementsAre(IsInvalid(), IsReg())));
  111. EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode));
  112. // TODO: check that the two instructions alias each other.
  113. }
  114. TEST_F(LatencySnippetGeneratorTest, LAHF) {
  115. const unsigned Opcode = llvm::X86::LAHF;
  116. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  117. EXPECT_THAT(CT.Info, HasSubstr("cycle through"));
  118. ASSERT_THAT(CT.Instructions, SizeIs(2));
  119. const InstructionTemplate &IT = CT.Instructions[0];
  120. EXPECT_THAT(IT.getOpcode(), Opcode);
  121. ASSERT_THAT(IT.VariableValues, SizeIs(0));
  122. }
  123. TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
  124. // BNDCL32rr is parallel no matter what.
  125. // explicit use 0 : reg RegClass=BNDR
  126. // explicit use 1 : reg RegClass=GR32
  127. const unsigned Opcode = llvm::X86::BNDCL32rr;
  128. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  129. EXPECT_THAT(CT.Info, HasSubstr("parallel"));
  130. ASSERT_THAT(CT.Instructions, SizeIs(1));
  131. const InstructionTemplate &IT = CT.Instructions[0];
  132. EXPECT_THAT(IT.getOpcode(), Opcode);
  133. ASSERT_THAT(IT.VariableValues, SizeIs(2));
  134. EXPECT_THAT(IT.VariableValues[0], IsInvalid());
  135. EXPECT_THAT(IT.VariableValues[1], IsInvalid());
  136. }
  137. TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
  138. // CDQ is serial no matter what.
  139. // implicit def : EAX
  140. // implicit def : EDX
  141. // implicit use : EAX
  142. const unsigned Opcode = llvm::X86::CDQ;
  143. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  144. EXPECT_THAT(CT.Info, HasSubstr("serial"));
  145. ASSERT_THAT(CT.Instructions, SizeIs(1));
  146. const InstructionTemplate &IT = CT.Instructions[0];
  147. EXPECT_THAT(IT.getOpcode(), Opcode);
  148. ASSERT_THAT(IT.VariableValues, SizeIs(0));
  149. }
  150. TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
  151. // CMOVA32rr has tied variables, we enumerate the possible values to execute
  152. // as many in parallel as possible.
  153. // explicit def 0 : reg RegClass=GR32
  154. // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
  155. // explicit use 2 : reg RegClass=GR32
  156. // implicit use : EFLAGS
  157. const unsigned Opcode = llvm::X86::CMOVA32rr;
  158. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  159. EXPECT_THAT(CT.Info, HasSubstr("static renaming"));
  160. constexpr const unsigned kInstructionCount = 15;
  161. ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount));
  162. std::unordered_set<unsigned> AllDefRegisters;
  163. for (const auto &IT : CT.Instructions) {
  164. ASSERT_THAT(IT.VariableValues, SizeIs(2));
  165. AllDefRegisters.insert(IT.VariableValues[0].getReg());
  166. }
  167. EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
  168. << "Each instruction writes to a different register";
  169. }
  170. TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
  171. // CMOV_GR32 has no tied variables, we make sure def and use are different
  172. // from each other.
  173. // explicit def 0 : reg RegClass=GR32
  174. // explicit use 1 : reg RegClass=GR32
  175. // explicit use 2 : reg RegClass=GR32
  176. // explicit use 3 : imm
  177. // implicit use : EFLAGS
  178. const unsigned Opcode = llvm::X86::CMOV_GR32;
  179. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  180. EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
  181. ASSERT_THAT(CT.Instructions, SizeIs(1));
  182. const InstructionTemplate &IT = CT.Instructions[0];
  183. EXPECT_THAT(IT.getOpcode(), Opcode);
  184. ASSERT_THAT(IT.VariableValues, SizeIs(4));
  185. EXPECT_THAT(IT.VariableValues[0].getReg(), Not(IT.VariableValues[1].getReg()))
  186. << "Def is different from first Use";
  187. EXPECT_THAT(IT.VariableValues[0].getReg(), Not(IT.VariableValues[2].getReg()))
  188. << "Def is different from second Use";
  189. EXPECT_THAT(IT.VariableValues[3], IsInvalid());
  190. }
  191. TEST_F(UopsSnippetGeneratorTest, MemoryUse) {
  192. // Mov32rm reads from memory.
  193. const unsigned Opcode = llvm::X86::MOV32rm;
  194. const CodeTemplate CT = checkAndGetCodeTemplate(Opcode);
  195. EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
  196. ASSERT_THAT(CT.Instructions,
  197. SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses));
  198. const InstructionTemplate &IT = CT.Instructions[0];
  199. EXPECT_THAT(IT.getOpcode(), Opcode);
  200. ASSERT_THAT(IT.VariableValues, SizeIs(6));
  201. EXPECT_EQ(IT.VariableValues[2].getImm(), 1);
  202. EXPECT_EQ(IT.VariableValues[3].getReg(), 0u);
  203. EXPECT_EQ(IT.VariableValues[4].getImm(), 0);
  204. EXPECT_EQ(IT.VariableValues[5].getReg(), 0u);
  205. }
  206. TEST_F(UopsSnippetGeneratorTest, MemoryUse_Movsb) {
  207. // MOVSB writes to scratch memory register.
  208. const unsigned Opcode = llvm::X86::MOVSB;
  209. const Instruction Instr(State, Opcode);
  210. auto Error = Generator.generateCodeTemplate(Instr).takeError();
  211. EXPECT_TRUE((bool)Error);
  212. llvm::consumeError(std::move(Error));
  213. }
  214. class FakeSnippetGenerator : public SnippetGenerator {
  215. public:
  216. FakeSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {}
  217. Instruction createInstruction(unsigned Opcode) {
  218. return Instruction(State, Opcode);
  219. }
  220. private:
  221. llvm::Expected<CodeTemplate>
  222. generateCodeTemplate(const Instruction &Instr) const override {
  223. return llvm::make_error<llvm::StringError>("not implemented",
  224. llvm::inconvertibleErrorCode());
  225. }
  226. };
  227. using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeSnippetGenerator>;
  228. testing::Matcher<const RegisterValue &> IsRegisterValue(unsigned Reg,
  229. llvm::APInt Value) {
  230. return testing::AllOf(testing::Field(&RegisterValue::Register, Reg),
  231. testing::Field(&RegisterValue::Value, Value));
  232. }
  233. TEST_F(FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd16ri) {
  234. // ADD16ri:
  235. // explicit def 0 : reg RegClass=GR16
  236. // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
  237. // explicit use 2 : imm
  238. // implicit def : EFLAGS
  239. InstructionTemplate IT(Generator.createInstruction(llvm::X86::ADD16ri));
  240. IT.getValueFor(IT.Instr.Variables[0]) =
  241. llvm::MCOperand::createReg(llvm::X86::AX);
  242. std::vector<InstructionTemplate> Snippet;
  243. Snippet.push_back(std::move(IT));
  244. const auto RIV = Generator.computeRegisterInitialValues(Snippet);
  245. EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::AX, llvm::APInt())));
  246. }
  247. TEST_F(FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd64rr) {
  248. // ADD64rr:
  249. // mov64ri rax, 42
  250. // add64rr rax, rax, rbx
  251. // -> only rbx needs defining.
  252. std::vector<InstructionTemplate> Snippet;
  253. {
  254. InstructionTemplate Mov(Generator.createInstruction(llvm::X86::MOV64ri));
  255. Mov.getValueFor(Mov.Instr.Variables[0]) =
  256. llvm::MCOperand::createReg(llvm::X86::RAX);
  257. Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
  258. Snippet.push_back(std::move(Mov));
  259. }
  260. {
  261. InstructionTemplate Add(Generator.createInstruction(llvm::X86::ADD64rr));
  262. Add.getValueFor(Add.Instr.Variables[0]) =
  263. llvm::MCOperand::createReg(llvm::X86::RAX);
  264. Add.getValueFor(Add.Instr.Variables[1]) =
  265. llvm::MCOperand::createReg(llvm::X86::RBX);
  266. Snippet.push_back(std::move(Add));
  267. }
  268. const auto RIV = Generator.computeRegisterInitialValues(Snippet);
  269. EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(llvm::X86::RBX, llvm::APInt())));
  270. }
  271. } // namespace
  272. } // namespace exegesis