Index: tools/llvm-exegesis/lib/Assembler.h =================================================================== --- tools/llvm-exegesis/lib/Assembler.h +++ tools/llvm-exegesis/lib/Assembler.h @@ -33,6 +33,8 @@ namespace exegesis { +class ExegesisTarget; + // Gather the set of reserved registers (depends on function's calling // convention and target machine). llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); @@ -41,7 +43,9 @@ // Instructions. Runs a set of llvm Passes to provide correct prologue and // epilogue. Once the MachineFunction is ready, it is assembled for TM to // AsmStream, the temporary function is eventually discarded. -void assembleToStream(std::unique_ptr TM, +void assembleToStream(const ExegesisTarget* ET, + std::unique_ptr TM, + llvm::ArrayRef RegsToDef, llvm::ArrayRef Instructions, llvm::raw_pwrite_stream &AsmStream); Index: tools/llvm-exegesis/lib/Assembler.cpp =================================================================== --- tools/llvm-exegesis/lib/Assembler.cpp +++ tools/llvm-exegesis/lib/Assembler.cpp @@ -28,6 +28,21 @@ static constexpr const char ModuleID[] = "ExegesisInfoTest"; static constexpr const char FunctionID[] = "foo"; +static std::vector +generateSnippetSetupCode(const llvm::ArrayRef RegsToDef, + const ExegesisTarget &ET, bool &IsComplete) { + IsComplete = true; + std::vector Result; + for (const unsigned Reg : RegsToDef) { + // Load a constant in the register. + const auto Code = ET.setRegToConstant(Reg); + if (Code.empty()) + IsComplete = false; + Result.insert(Result.end(), Code.begin(), Code.end()); + } + return Result; +} + // Small utility function to add named passes. static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, llvm::TargetPassConfig &TPC) { @@ -123,7 +138,9 @@ return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF); } -void assembleToStream(std::unique_ptr TM, +void assembleToStream(const ExegesisTarget *ET, + std::unique_ptr TM, + llvm::ArrayRef RegsToDef, llvm::ArrayRef Instructions, llvm::raw_pwrite_stream &AsmStream) { std::unique_ptr Context = @@ -140,9 +157,20 @@ auto &Properties = MF.getProperties(); Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs); Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA); - // FIXME: Remove this when we assign all used registers as config step. This - // essentially disables checks that used registers are def'ed somewhere. - Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); + std::vector SnippetWithSetup; + bool IsSnippetSetupComplete; + if (ET) { + SnippetWithSetup = + generateSnippetSetupCode(RegsToDef, *ET, IsSnippetSetupComplete); + SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(), + Instructions.end()); + Instructions = SnippetWithSetup; + } + // If the snippet setup is not complete, we disable liveliness tracking. This + // means that we won't know what values are in the registers. + if (!IsSnippetSetupComplete) + Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); + // prologue/epilogue pass needs the reserved registers to be frozen, this // is usually done by the SelectionDAGISel pass. MF.getRegInfo().freezeReservedRegs(MF); @@ -162,7 +190,7 @@ PM.add(MMI.release()); TPC->printAndVerify("MachineFunctionGenerator::assemble"); // Add target-specific passes. - if (const auto *ET = ExegesisTarget::lookup(TM->getTargetTriple())) { + if (ET) { ET->addTargetSpecificPasses(PM); TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses"); } Index: tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.h +++ tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -39,7 +39,10 @@ // This code is run before the Snippet is iterated. Since it is part of the // measurement it should be as short as possible. It is usually used to setup // the content of the Registers. - std::vector SnippetSetup; + struct Setup { + std::vector RegsToDef; + }; + Setup SnippetSetup; // The sequence of instructions that are to be repeated. std::vector Snippet; @@ -71,6 +74,10 @@ run(unsigned Opcode, const InstructionFilter &Filter, unsigned NumRepetitions); + // Given a snippet, computes which registers the setup code needs to define. + std::vector + computeRegsToDef(const std::vector &Snippet) const; + protected: const LLVMState &State; const llvm::MCInstrInfo &MCInstrInfo; @@ -96,9 +103,8 @@ const unsigned NumRepetitions) const = 0; llvm::Expected - writeObjectFile(llvm::ArrayRef Code) const; - llvm::Expected - createExecutableFunction(llvm::ArrayRef Code) const; + writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef Code) const; }; } // namespace exegesis Index: tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -78,10 +78,11 @@ // Repeat the snippet until there are at least NumInstructions in the // resulting code. The snippet is always repeated at least once. - const auto GenerateInstructions = [&Snippet](const int MinInstructions) { - std::vector Code = Snippet; + const auto GenerateInstructions = [&Configuration]( + const int MinInstructions) { + std::vector Code = Configuration.Snippet; for (int I = 0; I < MinInstructions; ++I) - Code.push_back(Snippet[I % Snippet.size()]); + Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]); return Code; }; @@ -90,8 +91,8 @@ // that the inside instructions are repeated. constexpr const int kMinInstructionsForSnippet = 16; { - auto ObjectFilePath = - writeObjectFile(GenerateInstructions(kMinInstructionsForSnippet)); + auto ObjectFilePath = writeObjectFile( + Configuration.SnippetSetup, GenerateInstructions(kMinInstructionsForSnippet)); if (llvm::Error E = ObjectFilePath.takeError()) { InstrBenchmark.Error = llvm::toString(std::move(E)); return InstrBenchmark; @@ -104,8 +105,8 @@ // Assemble NumRepetitions instructions repetitions of the snippet for // measurements. - auto ObjectFilePath = - writeObjectFile(GenerateInstructions(InstrBenchmark.NumRepetitions)); + auto ObjectFilePath = writeObjectFile( + Configuration.SnippetSetup, GenerateInstructions(InstrBenchmark.NumRepetitions)); if (llvm::Error E = ObjectFilePath.takeError()) { InstrBenchmark.Error = llvm::toString(std::move(E)); return InstrBenchmark; @@ -126,22 +127,67 @@ // TODO: Generate as many configurations as needed here. BenchmarkConfiguration Configuration; Configuration.Info = Prototype.Explanation; - for (InstructionInstance &II : Prototype.Snippet) - Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild()); + for (InstructionInstance &II : Prototype.Snippet) { + II.randomizeUnsetVariables(); + Configuration.Snippet.push_back(II.build()); + } + Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet); return std::vector{Configuration}; } else return E.takeError(); } +std::vector BenchmarkRunner::computeRegsToDef( + const std::vector &Snippet) const { + // Collect all register uses and create a mov for each of them. + // Loop invariant: DefinedRegs[i] is true iif it has been set at least once + // before the current instruction. + llvm::BitVector DefinedRegs = RATC.emptyRegisters(); + std::vector RegsToDef; + for (const InstructionInstance &II : Snippet) { + // Returns the register that this Operand sets or uses, or 0 if this is not + // a register. + const auto GetOpReg = [&II](const Operand &Op) -> unsigned { + if (Op.ImplicitReg) { + return *Op.ImplicitReg; + } else if (Op.IsExplicit && II.getValueFor(Op).isReg()) { + return II.getValueFor(Op).getReg(); + } + return 0; + }; + // Collect used registers that have never been def'ed. + for (const Operand &Op : II.Instr.Operands) { + if (!Op.IsDef) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0 && !DefinedRegs.test(Reg)) { + RegsToDef.push_back(Reg); + DefinedRegs.set(Reg); + } + } + } + // Mark defs as having been def'ed. + for (const Operand &Op : II.Instr.Operands) { + if (Op.IsDef) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0) { + DefinedRegs.set(Reg); + } + } + } + } + return RegsToDef; +} + llvm::Expected -BenchmarkRunner::writeObjectFile(llvm::ArrayRef Code) const { +BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef Code) const { int ResultFD = 0; llvm::SmallString<256> ResultPath; if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile( "snippet", "o", ResultFD, ResultPath))) return std::move(E); llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); - assembleToStream(State.createTargetMachine(), Code, OFS); + assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), Setup.RegsToDef, Code, OFS); return ResultPath.str(); } Index: tools/llvm-exegesis/lib/LlvmState.h =================================================================== --- tools/llvm-exegesis/lib/LlvmState.h +++ tools/llvm-exegesis/lib/LlvmState.h @@ -21,6 +21,7 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Target/TargetMachine.h" +#include "Target.h" #include #include Index: tools/llvm-exegesis/lib/LlvmState.cpp =================================================================== --- tools/llvm-exegesis/lib/LlvmState.cpp +++ tools/llvm-exegesis/lib/LlvmState.cpp @@ -29,6 +29,7 @@ TargetMachine.reset(static_cast( TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options, llvm::Reloc::Model::Static))); + TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple()); } LLVMState::LLVMState() Index: tools/llvm-exegesis/lib/MCInstrDescView.h =================================================================== --- tools/llvm-exegesis/lib/MCInstrDescView.h +++ tools/llvm-exegesis/lib/MCInstrDescView.h @@ -93,12 +93,17 @@ unsigned getOpcode() const; llvm::MCOperand &getValueFor(const Variable &Var); + const llvm::MCOperand &getValueFor(const Variable &Var) const; llvm::MCOperand &getValueFor(const Operand &Op); + const llvm::MCOperand &getValueFor(const Operand &Op) const; bool hasImmediateVariables() const; - // Assigns a Random Value to all Variables that are still Invalid and returns - // the instance as an llvm::MCInst. - llvm::MCInst randomizeUnsetVariablesAndBuild(); + // Assigns a Random Value to all Variables that are still Invalid. + void randomizeUnsetVariables(); + + // Returns the instance as an llvm::MCInst. The InstructionInstance must be + // fully allocated (no invalid variables). + llvm::MCInst build() const; Instruction Instr; llvm::SmallVector VariableValues; Index: tools/llvm-exegesis/lib/MCInstrDescView.cpp =================================================================== --- tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -99,11 +99,20 @@ return VariableValues[Var.Index]; } +const llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) const { + return VariableValues[Var.Index]; +} + llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) { assert(Op.VariableIndex >= 0); return getValueFor(Instr.Variables[Op.VariableIndex]); } +const llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) const { + assert(Op.VariableIndex >= 0); + return getValueFor(Instr.Variables[Op.VariableIndex]); +} + // forward declaration. static void randomize(const Instruction &Instr, const Variable &Var, llvm::MCOperand &AssignedValue); @@ -118,12 +127,15 @@ }); } -llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() { +void InstructionInstance::randomizeUnsetVariables() { for (const Variable &Var : Instr.Variables) { llvm::MCOperand &AssignedValue = getValueFor(Var); if (!AssignedValue.isValid()) randomize(Instr, Var, AssignedValue); } +} + +llvm::MCInst InstructionInstance::build() const { llvm::MCInst Result; Result.setOpcode(Instr.Description->Opcode); for (const auto &Op : Instr.Operands) Index: tools/llvm-exegesis/lib/Target.h =================================================================== --- tools/llvm-exegesis/lib/Target.h +++ tools/llvm-exegesis/lib/Target.h @@ -20,6 +20,8 @@ #include "llvm/ADT/Triple.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" namespace exegesis { @@ -28,6 +30,12 @@ // Targets can use this to add target-specific passes in assembleToStream(); virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {} + // Generates code to move a constant into a the given register. + virtual std::vector + setRegToConstant(unsigned Reg) const { + return {}; + } + // Returns the ExegesisTarget for the given triple or nullptr if the target // does not exist. static const ExegesisTarget *lookup(llvm::Triple TT); Index: tools/llvm-exegesis/lib/X86/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/X86/Target.cpp +++ tools/llvm-exegesis/lib/X86/Target.cpp @@ -8,7 +8,10 @@ //===----------------------------------------------------------------------===// #include "../Target.h" +#include "MCTargetDesc/X86MCTargetDesc.h" #include "X86.h" +#include "X86RegisterInfo.h" +#include "llvm/MC/MCInstBuilder.h" namespace exegesis { @@ -22,9 +25,87 @@ // PM.add(llvm::createX86FloatingPointStackifierPass()); } + std::vector + setRegToConstant(const unsigned Reg) const override { + // FIXME: Handle FP stack: + // llvm::X86::RFP32RegClass + // llvm::X86::RFP64RegClass + // llvm::X86::RFP80RegClass + if (llvm::X86::GR8RegClass.contains(Reg)) { + return {llvm::MCInstBuilder(llvm::X86::MOV8ri).addReg(Reg).addImm(1)}; + } + if (llvm::X86::GR16RegClass.contains(Reg)) { + return {llvm::MCInstBuilder(llvm::X86::MOV16ri).addReg(Reg).addImm(1)}; + } + if (llvm::X86::GR32RegClass.contains(Reg)) { + return {llvm::MCInstBuilder(llvm::X86::MOV32ri).addReg(Reg).addImm(1)}; + } + if (llvm::X86::GR64RegClass.contains(Reg)) { + return {llvm::MCInstBuilder(llvm::X86::MOV64ri32).addReg(Reg).addImm(1)}; + } + if (llvm::X86::VR128XRegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm); + } + if (llvm::X86::VR256XRegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm); + } + if (llvm::X86::VR512RegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU64Zrm); + } + return {}; + } + bool matchesArch(llvm::Triple::ArchType Arch) const override { return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86; } + +private: + // setRegToConstant() specialized for a vector register of size + // `RegSizeBytes`. `RMOpcode` is the opcode used to do a memory -> vector + // register load. + static std::vector + setVectorRegToConstant(const unsigned Reg, const unsigned RegSizeBytes, const unsigned RMOpcode) { + // There is no instruction to directly set XMM, go through memory. + // Since vector values can be interpreted as integers of various sizes (8 + // to 64 bits) as well as floats and double, so we chose an immediate + // value that has set bits for all byte values and is a normal float/ + // double. 0x40404040 is ~32.5 when interpreted as a double and ~3.0f when + // interpreted as a float. + constexpr const uint64_t kImmValue = 0x40404040ull; + std::vector Result; + // Allocate scratch memory on the stack. + Result.push_back(llvm::MCInstBuilder(llvm::X86::SUB64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(RegSizeBytes)); + // Fill scratch memory. + for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += 4) { + Result.push_back(llvm::MCInstBuilder(llvm::X86::MOV32mi) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(Disp) // Disp + .addReg(0) // Segment + // Immediate. + .addImm(kImmValue)); + } + // Load Reg from scratch memory. + Result.push_back(llvm::MCInstBuilder(RMOpcode) + .addReg(Reg) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0)); // Segment + // Release scratch memory. + Result.push_back(llvm::MCInstBuilder(llvm::X86::ADD64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(RegSizeBytes)); + return Result; + } }; } // namespace Index: unittests/tools/llvm-exegesis/Common/AssemblerUtils.h =================================================================== --- unittests/tools/llvm-exegesis/Common/AssemblerUtils.h +++ unittests/tools/llvm-exegesis/Common/AssemblerUtils.h @@ -36,9 +36,16 @@ } template inline void Check(llvm::MCInst MCInst, Bs... Bytes) { - ExecutableFunction Function = (MCInst.getOpcode() == 0) - ? assembleToFunction({}) - : assembleToFunction({MCInst}); + CheckWithSetup(nullptr, {}, MCInst, Bytes...); + } + + template + inline void CheckWithSetup(const ExegesisTarget *ET, + llvm::ArrayRef RegsToDef, + llvm::MCInst MCInst, Bs... Bytes) { + ExecutableFunction Function = + (MCInst.getOpcode() == 0) ? assembleToFunction(ET, RegsToDef, {}) + : assembleToFunction(ET, RegsToDef, {MCInst}); ASSERT_THAT(Function.getFunctionBytes().str(), testing::ElementsAre(Bytes...)); if (CanExecute) @@ -60,10 +67,13 @@ } ExecutableFunction - assembleToFunction(llvm::ArrayRef Instructions) { + assembleToFunction(const ExegesisTarget *ET, + llvm::ArrayRef RegsToDef, + llvm::ArrayRef Instructions) { llvm::SmallString<256> Buffer; llvm::raw_svector_ostream AsmStream(Buffer); - assembleToStream(createTargetMachine(), Instructions, AsmStream); + assembleToStream(ET, createTargetMachine(), RegsToDef, Instructions, + AsmStream); return ExecutableFunction(createTargetMachine(), getObjectFromBuffer(AsmStream.str())); } Index: unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -25,6 +25,7 @@ using testing::HasSubstr; using testing::Not; using testing::SizeIs; +using testing::UnorderedElementsAre; MATCHER(IsInvalid, "") { return !arg.isValid(); } MATCHER(IsReg, "") { return arg.isReg(); } @@ -214,5 +215,75 @@ EXPECT_THAT(II.VariableValues[3], IsInvalid()); } +class FakeBenchmarkRunner : public BenchmarkRunner { +public: + using BenchmarkRunner::BenchmarkRunner; + + Instruction createInstruction(unsigned Opcode) { + return Instruction(MCInstrInfo.get(Opcode), RATC); + } + +private: + InstructionBenchmark::ModeE getMode() const override { + return InstructionBenchmark::Unknown; + } + + llvm::Expected + generatePrototype(unsigned Opcode) const override { + return llvm::make_error("not implemented", + llvm::inconvertibleErrorCode()); + } + + std::vector + runMeasurements(const ExecutableFunction &EF, + const unsigned NumRepetitions) const override { + return {}; + } +}; + +using FakeSnippetGeneratorTest = SnippetGeneratorTest; + +TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) { + // ADD16ri: + // explicit def 0 : reg RegClass=GR16 + // explicit use 1 : reg RegClass=GR16 | TIED_TO:0 + // explicit use 2 : imm + // implicit def : EFLAGS + InstructionInstance II(Runner.createInstruction(llvm::X86::ADD16ri)); + // II.randomizeUnsetVariables(); + II.getValueFor(II.Instr.Variables[0]) = + llvm::MCOperand::createReg(llvm::X86::AX); + std::vector Snippet; + Snippet.push_back(std::move(II)); + const auto RegsToDef = Runner.computeRegsToDef(Snippet); + EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX)); +} + +TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) { + // ADD64rr: + // mov64ri rax, 42 + // add64rr rax, rax, rbx + // -> only rbx needs defining. + std::vector Snippet; + { + InstructionInstance Mov(Runner.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)); + } + { + InstructionInstance Add(Runner.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 RegsToDef = Runner.computeRegsToDef(Snippet); + EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX)); +} + } // namespace } // namespace exegesis Index: unittests/tools/llvm-exegesis/X86/TargetTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/TargetTest.cpp +++ unittests/tools/llvm-exegesis/X86/TargetTest.cpp @@ -3,6 +3,7 @@ #include #include +#include "MCTargetDesc/X86MCTargetDesc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -13,15 +14,31 @@ namespace { using testing::NotNull; +using testing::SizeIs; +using testing::Not; +using testing::IsEmpty; class X86TargetTest : public ::testing::Test { protected: + X86TargetTest() + : Target_(ExegesisTarget::lookup(llvm::Triple("x86_64-unknown-linux"))) { + EXPECT_THAT(Target_, NotNull()); + } static void SetUpTestCase() { InitializeX86ExegesisTarget(); } + + const ExegesisTarget *const Target_; }; -TEST_F(X86TargetTest, Lookup) { - EXPECT_THAT(ExegesisTarget::lookup(llvm::Triple("x86_64-unknown-linux")), - NotNull()); +TEST_F(X86TargetTest, SetRegToConstantGPR) { + const auto Insts = Target_->setRegToConstant(llvm::X86::EAX); + EXPECT_THAT(Insts, SizeIs(1)); + EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::MOV32ri); + EXPECT_EQ(Insts[0].getOperand(0).getReg(), llvm::X86::EAX); +} + +TEST_F(X86TargetTest, SetRegToConstantXMM) { + const auto Insts = Target_->setRegToConstant(llvm::X86::XMM1); + EXPECT_THAT(Insts, Not(IsEmpty())); } } // namespace