Index: tools/llvm-exegesis/lib/AArch64/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/AArch64/Target.cpp +++ tools/llvm-exegesis/lib/AArch64/Target.cpp @@ -27,6 +27,25 @@ }; class ExegesisAArch64Target : public ExegesisTarget { + std::vector setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override { + llvm_unreachable("Not yet implemented"); + } + + unsigned getScratchMemoryRegister(const llvm::Triple &) const override { + llvm_unreachable("Not yet implemented"); + } + + void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg, + unsigned Offset) const override { + llvm_unreachable("Not yet implemented"); + } + + unsigned getMaxMemoryAccessSize() const override { + llvm_unreachable("Not yet implemented"); + } + bool matchesArch(llvm::Triple::ArchType Arch) const override { return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be; } Index: tools/llvm-exegesis/lib/Assembler.h =================================================================== --- tools/llvm-exegesis/lib/Assembler.h +++ tools/llvm-exegesis/lib/Assembler.h @@ -39,6 +39,11 @@ // convention and target machine). llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); +struct RegisterValue { + unsigned Register; + llvm::APInt Value; +}; + // Creates a temporary `void foo(char*)` function containing the provided // Instructions. Runs a set of llvm Passes to provide correct prologue and // epilogue. Once the MachineFunction is ready, it is assembled for TM to @@ -46,7 +51,7 @@ void assembleToStream(const ExegesisTarget &ET, std::unique_ptr TM, llvm::ArrayRef LiveIns, - llvm::ArrayRef RegsToDef, + llvm::ArrayRef RegisterInitialValues, 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 @@ -34,13 +34,13 @@ const llvm::LLVMTargetMachine &TM, bool &IsComplete) { IsComplete = true; std::vector Result; - for (const unsigned Reg : RegsToDef) { - // Load a constant in the register. - const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg); - if (Code.empty()) - IsComplete = false; - Result.insert(Result.end(), Code.begin(), Code.end()); - } + // for (const unsigned Reg : RegsToDef) { + // // Load a constant in the register. + // const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg); + // if (Code.empty()) + // IsComplete = false; + // Result.insert(Result.end(), Code.begin(), Code.end()); + // } return Result; } @@ -149,7 +149,7 @@ void assembleToStream(const ExegesisTarget &ET, std::unique_ptr TM, llvm::ArrayRef LiveIns, - llvm::ArrayRef RegsToDef, + llvm::ArrayRef RegisterInitialValues, llvm::ArrayRef Instructions, llvm::raw_pwrite_stream &AsmStream) { std::unique_ptr Context = @@ -170,25 +170,21 @@ for (const unsigned Reg : LiveIns) MF.getRegInfo().addLiveIn(Reg); - bool IsSnippetSetupComplete = false; - std::vector SnippetWithSetup = - generateSnippetSetupCode(RegsToDef, ET, *TM, IsSnippetSetupComplete); - if (!SnippetWithSetup.empty()) { - SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(), - Instructions.end()); - Instructions = SnippetWithSetup; + std::vector Code; + const llvm::MCSubtargetInfo *const MSI = TM->getMCSubtargetInfo(); + for (const RegisterValue &RV : RegisterInitialValues) { + const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value); + Code.insert(Code.end(), SetRegisterCode.begin(), SetRegisterCode.end()); } - // 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); + + Code.insert(Code.end(), Instructions.begin(), Instructions.end()); // prologue/epilogue pass needs the reserved registers to be frozen, this // is usually done by the SelectionDAGISel pass. MF.getRegInfo().freezeReservedRegs(MF); // Fill the MachineFunction from the instructions. - fillMachineFunction(MF, LiveIns, Instructions); + fillMachineFunction(MF, LiveIns, Code); // We create the pass manager, run the passes to populate AsmBuffer. llvm::MCContext &MCContext = MMI->getContext(); Index: tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.h +++ tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -44,6 +44,7 @@ // Before the code is executed some instructions are added to setup the // registers initial values. std::vector RegsToDef; + std::vector RegisterInitialValues; // We also need to provide the registers that are live on entry for the // assembler to generate proper prologue/epilogue. Index: tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -200,7 +200,7 @@ return std::move(E); llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), - BC.LiveIns, BC.RegsToDef, Code, OFS); + BC.LiveIns, BC.RegisterInitialValues, Code, OFS); return ResultPath.str(); } Index: tools/llvm-exegesis/lib/CMakeLists.txt =================================================================== --- tools/llvm-exegesis/lib/CMakeLists.txt +++ tools/llvm-exegesis/lib/CMakeLists.txt @@ -19,6 +19,7 @@ MCInstrDescView.cpp PerfHelper.cpp RegisterAliasing.cpp + RegisterValue.cpp Target.cpp Uops.cpp ) Index: tools/llvm-exegesis/lib/RegisterValue.h =================================================================== --- /dev/null +++ tools/llvm-exegesis/lib/RegisterValue.h @@ -0,0 +1,37 @@ +//===-- RegisterValue.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Defines a Target independent value for a Register. This is useful to explore +/// the influence of the instruction input values on its execution time. +/// +//===----------------------------------------------------------------------===// + +#include + +namespace exegesis { + +enum class PredefinedValues { + POS_ZERO, // Positive zero + NEG_ZERO, // Negative zero + ONE, // 1.0 + INF, // Infinity + QNAN, // Quiet NaN + ULP, // One Unit in the last place + SMALLEST = ULP, // The minimum subnormal number + SMALLEST_NORM, // The minimum normal number + LARGEST, // The maximum normal number + ONE_PLUS_ULP, // The value just after 1.0 +}; + +llvm::APInt GetFloatValueAsInteger(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value); + +} // namespace exegesis Index: tools/llvm-exegesis/lib/RegisterValue.cpp =================================================================== --- /dev/null +++ tools/llvm-exegesis/lib/RegisterValue.cpp @@ -0,0 +1,37 @@ +#include "RegisterValue.h" +#include "llvm/ADT/APFloat.h" + +namespace exegesis { + +static llvm::APFloat GetFloatValue(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value) { + switch (Value) { + case PredefinedValues::POS_ZERO: + return llvm::APFloat::getZero(FltSemantics); + case PredefinedValues::NEG_ZERO: + return llvm::APFloat::getZero(FltSemantics, true); + case PredefinedValues::ONE: + return llvm::APFloat(FltSemantics, "1"); + case PredefinedValues::INF: + return llvm::APFloat::getInf(FltSemantics); + case PredefinedValues::QNAN: + return llvm::APFloat::getQNaN(FltSemantics); + case PredefinedValues::SMALLEST_NORM: + return llvm::APFloat::getSmallestNormalized(FltSemantics); + case PredefinedValues::LARGEST: + return llvm::APFloat::getLargest(FltSemantics); + case PredefinedValues::ULP: + return llvm::APFloat::getSmallest(FltSemantics); + case PredefinedValues::ONE_PLUS_ULP: + auto Output = GetFloatValue(FltSemantics, PredefinedValues::ONE); + Output.next(false); + return Output; + } +} + +llvm::APInt GetFloatValueAsInteger(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value) { + return GetFloatValue(FltSemantics, Value).bitcastToAPInt(); +} + +} // namespace exegesis Index: tools/llvm-exegesis/lib/Target.h =================================================================== --- tools/llvm-exegesis/lib/Target.h +++ tools/llvm-exegesis/lib/Target.h @@ -20,6 +20,7 @@ #include "BenchmarkResult.h" #include "BenchmarkRunner.h" #include "LlvmState.h" +#include "RegisterValue.h" #include "llvm/ADT/Triple.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/CallingConv.h" @@ -36,30 +37,24 @@ // Generates code to move a constant into a the given register. virtual std::vector - setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const { - return {}; - } + setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg, + const llvm::APInt &Value) const = 0; - // Returns the register pointing to scratch memory, or 0 if this target does - // not support memory operands. The benchmark function uses the default - // calling convention. - virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const { - return 0; - } + // Returns the register pointing to scratch memory, or 0 if this target + // does not support memory operands. The benchmark function uses the + // default calling convention. + virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const = 0; // Fills memory operands with references to the address at [Reg] + Offset. virtual void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg, - unsigned Offset) const { - llvm_unreachable( - "fillMemoryOperands() requires getScratchMemoryRegister() > 0"); - } + unsigned Offset) const = 0; // Returns the maximum number of bytes a load/store instruction can access at // once. This is typically the size of the largest register available on the // processor. Note that this only used as a hint to generate independant // load/stores to/from memory, so the exact returned value does not really // matter as long as it's large enough. - virtual unsigned getMaxMemoryAccessSize() const { return 0; } + virtual unsigned getMaxMemoryAccessSize() const = 0; // Creates a benchmark runner for the given mode. std::unique_ptr Index: tools/llvm-exegesis/lib/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/Target.cpp +++ tools/llvm-exegesis/lib/Target.cpp @@ -65,6 +65,25 @@ // Default implementation. class ExegesisDefaultTarget : public ExegesisTarget { private: + std::vector setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override { + llvm_unreachable("Not yet implemented"); + } + + unsigned getScratchMemoryRegister(const llvm::Triple &) const override { + llvm_unreachable("Not yet implemented"); + } + + void fillMemoryOperands(InstructionBuilder &IB, unsigned Reg, + unsigned Offset) const override { + llvm_unreachable("Not yet implemented"); + } + + unsigned getMaxMemoryAccessSize() const override { + llvm_unreachable("Not yet implemented"); + } + bool matchesArch(llvm::Triple::ArchType Arch) const override { llvm_unreachable("never called"); return false; Index: tools/llvm-exegesis/lib/X86/Target.cpp =================================================================== --- tools/llvm-exegesis/lib/X86/Target.cpp +++ tools/llvm-exegesis/lib/X86/Target.cpp @@ -99,6 +99,134 @@ } }; +static unsigned GetLoadImmediateOpcode(const llvm::APInt &Value) { + switch (Value.getBitWidth()) { + case 8: + return llvm::X86::MOV8ri; + case 16: + return llvm::X86::MOV16ri; + case 32: + return llvm::X86::MOV32ri; + case 64: + return llvm::X86::MOV64ri; + } + llvm_unreachable("Invalid Value Width"); +} + +// Generates instruction to load an immediate value into a register. +static llvm::MCInst loadImmediate(unsigned Reg, const llvm::APInt &Value, + unsigned MaxBitWidth) { + assert(Value.getBitWidth() <= MaxBitWidth && "Value too big to fit register"); + return llvm::MCInstBuilder(GetLoadImmediateOpcode(Value)) + .addReg(Reg) + .addImm(Value.getZExtValue()); +} + +// Allocates scratch memory on the stack. +static llvm::MCInst allocateStackSpace(unsigned Bytes) { + return llvm::MCInstBuilder(llvm::X86::SUB64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(Bytes); +} + +// Fills scratch memory at offset `OffsetBytes` with value `Imm`. +static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes, + uint64_t Imm) { + return llvm::MCInstBuilder(MovOpcode) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(OffsetBytes) // Disp + .addReg(0) // Segment + // Immediate. + .addImm(Imm); +} + +// Loads scratch memory into register `Reg` using opcode `RMOpcode`. +static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) { + return llvm::MCInstBuilder(RMOpcode) + .addReg(Reg) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0); // Segment +} + +// Releases scratch memory. +static llvm::MCInst releaseStackSpace(unsigned Bytes) { + return llvm::MCInstBuilder(llvm::X86::ADD64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(Bytes); +} + +// Reserves some space on the stack, fills it with the content of the provided +// constant and provide methods to load the stack value into a register. +struct ConstantInliner { + explicit ConstantInliner(const llvm::APInt &Constant) + : StackSize(Constant.getBitWidth() / 8) { + assert(Constant.getBitWidth() % 8 == 0 && "Must be a multiple of 8"); + add(allocateStackSpace(StackSize)); + size_t ByteOffset = 0; + for (; StackSize - ByteOffset >= 4; ByteOffset += 4) + add(fillStackSpace( + llvm::X86::MOV32mi, ByteOffset, + Constant.extractBits(32, ByteOffset * 8).getZExtValue())); + if (StackSize - ByteOffset >= 2) { + add(fillStackSpace( + llvm::X86::MOV16mi, ByteOffset, + Constant.extractBits(16, ByteOffset * 8).getZExtValue())); + ByteOffset += 2; + } + if (StackSize - ByteOffset >= 1) + add(fillStackSpace( + llvm::X86::MOV8mi, ByteOffset, + Constant.extractBits(8, ByteOffset * 8).getZExtValue())); + } + + std::vector loadAndFinalize(unsigned Reg, unsigned Opcode, + unsigned BitWidth) { + assert(StackSize * 8 == BitWidth && "Value does not have the correct size"); + add(loadToReg(Reg, Opcode)); + add(releaseStackSpace(StackSize)); + return std::move(Instructions); + } + + std::vector loadX87AndFinalize(unsigned Reg, unsigned Opcode, + unsigned BitWidth) { + assert(StackSize * 8 == BitWidth && "Value does not have the correct size"); + add(llvm::MCInstBuilder(Opcode) + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0)); // Segment + if (Reg != llvm::X86::ST0) + add(llvm::MCInstBuilder(llvm::X86::ST_Frr).addReg(Reg)); + add(releaseStackSpace(StackSize)); + return std::move(Instructions); + } + + std::vector popFlagAndFinalize() { + assert(StackSize * 8 == 64 && "Value does not have the correct size"); + add(llvm::MCInstBuilder(llvm::X86::POPF64)); + return std::move(Instructions); + } + +private: + ConstantInliner &add(const llvm::MCInst &Inst) { + Instructions.push_back(Inst); + return *this; + } + + const size_t StackSize; + std::vector Instructions; +}; + class ExegesisX86Target : public ExegesisTarget { void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override { // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F. @@ -147,50 +275,47 @@ } } - std::vector setRegToConstant(const llvm::MCSubtargetInfo &STI, - unsigned Reg) const override { - // GPR. + std::vector setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override { if (llvm::X86::GR8RegClass.contains(Reg)) - return {llvm::MCInstBuilder(llvm::X86::MOV8ri).addReg(Reg).addImm(1)}; + return {loadImmediate(Reg, Value, 8)}; if (llvm::X86::GR16RegClass.contains(Reg)) - return {llvm::MCInstBuilder(llvm::X86::MOV16ri).addReg(Reg).addImm(1)}; + return {loadImmediate(Reg, Value, 16)}; if (llvm::X86::GR32RegClass.contains(Reg)) - return {llvm::MCInstBuilder(llvm::X86::MOV32ri).addReg(Reg).addImm(1)}; + return {loadImmediate(Reg, Value, 32)}; if (llvm::X86::GR64RegClass.contains(Reg)) - return {llvm::MCInstBuilder(llvm::X86::MOV64ri32).addReg(Reg).addImm(1)}; - // MMX. + return {loadImmediate(Reg, Value, 64)}; + ConstantInliner CI(Value); if (llvm::X86::VR64RegClass.contains(Reg)) - return setVectorRegToConstant(Reg, 8, llvm::X86::MMX_MOVQ64rm); - // {X,Y,Z}MM. + return CI.loadAndFinalize(Reg, llvm::X86::MMX_MOVQ64rm, 64); if (llvm::X86::VR128XRegClass.contains(Reg)) { if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) - return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQU32Z128rm); + return CI.loadAndFinalize(Reg, llvm::X86::VMOVDQU32Z128rm, 128); if (STI.getFeatureBits()[llvm::X86::FeatureAVX]) - return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm); - return setVectorRegToConstant(Reg, 16, llvm::X86::MOVDQUrm); + return CI.loadAndFinalize(Reg, llvm::X86::VMOVDQUrm, 128); + return CI.loadAndFinalize(Reg, llvm::X86::MOVDQUrm, 128); } if (llvm::X86::VR256XRegClass.contains(Reg)) { if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) - return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQU32Z256rm); - return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm); + return CI.loadAndFinalize(Reg, llvm::X86::VMOVDQU32Z256rm, 256); + if (STI.getFeatureBits()[llvm::X86::FeatureAVX]) + return CI.loadAndFinalize(Reg, llvm::X86::VMOVDQUYrm, 256); } if (llvm::X86::VR512RegClass.contains(Reg)) - return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU32Zrm); - // X87. - if (llvm::X86::RFP32RegClass.contains(Reg) || - llvm::X86::RFP64RegClass.contains(Reg) || - llvm::X86::RFP80RegClass.contains(Reg)) - return setVectorRegToConstant(Reg, 8, llvm::X86::LD_Fp64m); - if (Reg == llvm::X86::EFLAGS) { - // Set all flags to 0 but the bits that are "reserved and set to 1". - constexpr const uint32_t kImmValue = 0x00007002u; - std::vector Result; - Result.push_back(allocateStackSpace(8)); - Result.push_back(fillStackSpace(llvm::X86::MOV64mi32, 0, kImmValue)); - Result.push_back(llvm::MCInstBuilder(llvm::X86::POPF64)); // Also pops. - return Result; + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return CI.loadAndFinalize(Reg, llvm::X86::VMOVDQU32Zrm, 512); + if (llvm::X86::RSTRegClass.contains(Reg)) { + if (Value.getBitWidth() == 32) + return CI.loadX87AndFinalize(Reg, llvm::X86::LD_F32m, 32); + if (Value.getBitWidth() == 64) + return CI.loadX87AndFinalize(Reg, llvm::X86::LD_F64m, 64); + if (Value.getBitWidth() == 80) + return CI.loadX87AndFinalize(Reg, llvm::X86::LD_F80m, 80); } - return {}; + if (Reg == llvm::X86::EFLAGS) + return CI.popFlagAndFinalize(); + llvm_unreachable("Not yet implemented"); } std::unique_ptr @@ -206,73 +331,6 @@ 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 uint32_t kImmValue = 0x40404040u; - std::vector Result; - Result.push_back(allocateStackSpace(RegSizeBytes)); - constexpr const unsigned kMov32NumBytes = 4; - for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += kMov32NumBytes) { - Result.push_back(fillStackSpace(llvm::X86::MOV32mi, Disp, kImmValue)); - } - Result.push_back(loadToReg(Reg, RMOpcode)); - Result.push_back(releaseStackSpace(RegSizeBytes)); - return Result; - } - - // Allocates scratch memory on the stack. - static llvm::MCInst allocateStackSpace(unsigned Bytes) { - return llvm::MCInstBuilder(llvm::X86::SUB64ri8) - .addReg(llvm::X86::RSP) - .addReg(llvm::X86::RSP) - .addImm(Bytes); - } - - // Fills scratch memory at offset `OffsetBytes` with value `Imm`. - static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes, - uint64_t Imm) { - return llvm::MCInstBuilder(MovOpcode) - // Address = ESP - .addReg(llvm::X86::RSP) // BaseReg - .addImm(1) // ScaleAmt - .addReg(0) // IndexReg - .addImm(OffsetBytes) // Disp - .addReg(0) // Segment - // Immediate. - .addImm(Imm); - } - - // Loads scratch memory into register `Reg` using opcode `RMOpcode`. - static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) { - return llvm::MCInstBuilder(RMOpcode) - .addReg(Reg) - // Address = ESP - .addReg(llvm::X86::RSP) // BaseReg - .addImm(1) // ScaleAmt - .addReg(0) // IndexReg - .addImm(0) // Disp - .addReg(0); // Segment - } - - // Releases scratch memory. - static llvm::MCInst releaseStackSpace(unsigned Bytes) { - return llvm::MCInstBuilder(llvm::X86::ADD64ri8) - .addReg(llvm::X86::RSP) - .addReg(llvm::X86::RSP) - .addImm(Bytes); - } }; } // namespace Index: unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp +++ unittests/tools/llvm-exegesis/AArch64/TargetTest.cpp @@ -41,13 +41,5 @@ const ExegesisTarget *const ExegesisTarget_; }; -TEST_F(AArch64TargetTest, SetRegToConstant) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "generic", "")); - // The AArch64 target currently doesn't know how to set register values - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::AArch64::X0); - EXPECT_THAT(Insts, SizeIs(0)); -} - } // namespace } // namespace exegesis Index: unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp +++ unittests/tools/llvm-exegesis/ARM/AssemblerTest.cpp @@ -30,12 +30,11 @@ }; TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunction) { - Check(ExegesisTarget::getDefault(), {}, llvm::MCInst(), 0x1e, 0xff, 0x2f, - 0xe1); + Check({}, llvm::MCInst(), 0x1e, 0xff, 0x2f, 0xe1); } TEST_F(ARMMachineFunctionGeneratorTest, DISABLED_JitFunctionADDrr) { - Check(ExegesisTarget::getDefault(), {llvm::ARM::R0}, + Check({{llvm::ARM::R0, llvm::APInt()}}, MCInstBuilder(llvm::ARM::ADDrr) .addReg(llvm::ARM::R0) .addReg(llvm::ARM::R0) Index: unittests/tools/llvm-exegesis/CMakeLists.txt =================================================================== --- unittests/tools/llvm-exegesis/CMakeLists.txt +++ unittests/tools/llvm-exegesis/CMakeLists.txt @@ -15,6 +15,7 @@ BenchmarkRunnerTest.cpp ClusteringTest.cpp PerfHelperTest.cpp + RegisterValueTest.cpp ) target_link_libraries(LLVMExegesisTests PRIVATE LLVMExegesis) Index: unittests/tools/llvm-exegesis/Common/AssemblerUtils.h =================================================================== --- unittests/tools/llvm-exegesis/Common/AssemblerUtils.h +++ unittests/tools/llvm-exegesis/Common/AssemblerUtils.h @@ -32,7 +32,9 @@ const std::string &CpuName) : TT(TT), CpuName(CpuName), CanExecute(llvm::Triple(TT).getArch() == - llvm::Triple(llvm::sys::getProcessTriple()).getArch()) { + llvm::Triple(llvm::sys::getProcessTriple()).getArch()), + ET(ExegesisTarget::lookup(llvm::Triple(TT))) { + assert(ET); if (!CanExecute) { llvm::outs() << "Skipping execution, host:" << llvm::sys::getProcessTriple() << ", target:" << TT @@ -41,12 +43,12 @@ } template - inline void Check(const ExegesisTarget &ET, - llvm::ArrayRef RegsToDef, llvm::MCInst MCInst, - Bs... Bytes) { + inline void Check(llvm::ArrayRef RegisterInitialValues, + llvm::MCInst MCInst, Bs... Bytes) { ExecutableFunction Function = - (MCInst.getOpcode() == 0) ? assembleToFunction(ET, RegsToDef, {}) - : assembleToFunction(ET, RegsToDef, {MCInst}); + (MCInst.getOpcode() == 0) + ? assembleToFunction(RegisterInitialValues, {}) + : assembleToFunction(RegisterInitialValues, {MCInst}); ASSERT_THAT(Function.getFunctionBytes().str(), testing::ElementsAre(Bytes...)); if (CanExecute) { @@ -70,14 +72,12 @@ } ExecutableFunction - assembleToFunction(const ExegesisTarget &ET, - llvm::ArrayRef RegsToDef, + assembleToFunction(llvm::ArrayRef RegisterInitialValues, llvm::ArrayRef Instructions) { llvm::SmallString<256> Buffer; llvm::raw_svector_ostream AsmStream(Buffer); - assembleToStream(ET, createTargetMachine(), /*LiveIns=*/{}, - RegsToDef, Instructions, - AsmStream); + assembleToStream(*ET, createTargetMachine(), /*LiveIns=*/{}, + RegisterInitialValues, Instructions, AsmStream); return ExecutableFunction(createTargetMachine(), getObjectFromBuffer(AsmStream.str())); } @@ -85,6 +85,7 @@ const std::string TT; const std::string CpuName; const bool CanExecute; + const ExegesisTarget *const ET; }; } // namespace exegesis Index: unittests/tools/llvm-exegesis/RegisterValueTest.cpp =================================================================== --- /dev/null +++ unittests/tools/llvm-exegesis/RegisterValueTest.cpp @@ -0,0 +1,68 @@ +//===-- RegisterValueTest.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterValue.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace exegesis { + +namespace { + +#define CHECK(EXPECTED, ACTUAL) \ + EXPECT_EQ(llvm::APInt(SizeInBits, EXPECTED, 16), \ + GetFloatValueAsInteger(Semantic, PredefinedValues::ACTUAL)) + +TEST(RegisterValueTest, Half) { + const size_t SizeInBits = 16; + const auto &Semantic = llvm::APFloatBase::IEEEhalf(); + CHECK("0000", POS_ZERO); + CHECK("8000", NEG_ZERO); + CHECK("3C00", ONE); + CHECK("7C00", INF); + CHECK("7E00", QNAN); + CHECK("7BFF", LARGEST); + CHECK("0400", SMALLEST_NORM); + CHECK("0001", SMALLEST); + CHECK("0001", ULP); + CHECK("3C01", ONE_PLUS_ULP); +} + +TEST(RegisterValueTest, Single) { + const size_t SizeInBits = 32; + const auto &Semantic = llvm::APFloatBase::IEEEsingle(); + CHECK("00000000", POS_ZERO); + CHECK("80000000", NEG_ZERO); + CHECK("3F800000", ONE); + CHECK("7F800000", INF); + CHECK("7FC00000", QNAN); + CHECK("7F7FFFFF", LARGEST); + CHECK("00800000", SMALLEST_NORM); + CHECK("00000001", SMALLEST); + CHECK("00000001", ULP); + CHECK("3F800001", ONE_PLUS_ULP); +} + +TEST(RegisterValueTest, Double) { + const size_t SizeInBits = 64; + const auto &Semantic = llvm::APFloatBase::IEEEdouble(); + CHECK("0000000000000000", POS_ZERO); + CHECK("8000000000000000", NEG_ZERO); + CHECK("3FF0000000000000", ONE); + CHECK("7FF0000000000000", INF); + CHECK("7FF8000000000000", QNAN); + CHECK("7FEFFFFFFFFFFFFF", LARGEST); + CHECK("0010000000000000", SMALLEST_NORM); + CHECK("0000000000000001", SMALLEST); + CHECK("0000000000000001", ULP); + CHECK("3FF0000000000001", ONE_PLUS_ULP); +} + +} // namespace +} // namespace exegesis Index: unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp +++ unittests/tools/llvm-exegesis/X86/AssemblerTest.cpp @@ -39,19 +39,12 @@ }; TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunction) { - Check(ExegesisTarget::getDefault(), {}, llvm::MCInst(), 0xc3); -} - -TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionXOR32rr_Default) { - Check(ExegesisTarget::getDefault(), {EAX}, - MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX), 0x31, 0xc0, - 0xc3); + Check({}, llvm::MCInst(), 0xc3); } TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionXOR32rr_X86) { - const auto *ET = ExegesisTarget::lookup(llvm::Triple("x86_64-unknown-linux")); - ASSERT_NE(ET, nullptr); - Check(*ET, {EAX}, MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX), + Check({{EAX, llvm::APInt(32, 1)}}, + MCInstBuilder(XOR32rr).addReg(EAX).addReg(EAX).addReg(EAX), // mov eax, 1 0xb8, 0x01, 0x00, 0x00, 0x00, // xor eax, eax @@ -59,15 +52,13 @@ } TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV64ri) { - Check(ExegesisTarget::getDefault(), {}, - MCInstBuilder(MOV64ri32).addReg(RAX).addImm(42), 0x48, 0xc7, 0xc0, 0x2a, - 0x00, 0x00, 0x00, 0xc3); + Check({}, MCInstBuilder(MOV64ri32).addReg(RAX).addImm(42), 0x48, 0xc7, 0xc0, + 0x2a, 0x00, 0x00, 0x00, 0xc3); } TEST_F(X86MachineFunctionGeneratorTest, DISABLED_JitFunctionMOV32ri) { - Check(ExegesisTarget::getDefault(), {}, - MCInstBuilder(MOV32ri).addReg(EAX).addImm(42), 0xb8, 0x2a, 0x00, 0x00, - 0x00, 0xc3); + Check({}, MCInstBuilder(MOV32ri).addReg(EAX).addImm(42), 0xb8, 0x2a, 0x00, + 0x00, 0x00, 0xc3); } } // namespace Index: unittests/tools/llvm-exegesis/X86/TargetTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/TargetTest.cpp +++ unittests/tools/llvm-exegesis/X86/TargetTest.cpp @@ -9,18 +9,103 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + +bool operator==(const MCOperand &a, const MCOperand &b) { + if (a.isImm() && b.isImm()) + return a.getImm() == b.getImm(); + if (a.isReg() && b.isReg()) + return a.getReg() == b.getReg(); + return false; +} + +bool operator==(const MCInst &a, const MCInst &b) { + if (a.getOpcode() != b.getOpcode()) + return false; + if (a.getNumOperands() != b.getNumOperands()) + return false; + for (unsigned I = 0; I < a.getNumOperands(); ++I) { + if (!(a.getOperand(I) == b.getOperand(I))) + return false; + } + return true; +} + +} // namespace llvm + namespace exegesis { void InitializeX86ExegesisTarget(); namespace { +using testing::AllOf; +using testing::ElementsAre; +using testing::ElementsAreArray; +using testing::Eq; using testing::Gt; +using testing::Matcher; using testing::NotNull; +using testing::Property; using testing::SizeIs; +using llvm::APInt; +using llvm::MCInst; +using llvm::MCInstBuilder; +using llvm::MCOperand; + +Matcher IsImm(int64_t Value) { + return AllOf(Property(&MCOperand::isImm, Eq(true)), + Property(&MCOperand::getImm, Eq(Value))); +} + +Matcher IsReg(unsigned Reg) { + return AllOf(Property(&MCOperand::isReg, Eq(true)), + Property(&MCOperand::getReg, Eq(Reg))); +} + +Matcher OpcodeIs(unsigned Opcode) { + return Property(&MCInst::getOpcode, Eq(Opcode)); +} + +Matcher IsMovImmediate(unsigned Opcode, int64_t Reg, int64_t Value) { + return AllOf(OpcodeIs(Opcode), ElementsAre(IsReg(Reg), IsImm(Value))); +} + +Matcher IsMovValueToStack(unsigned Opcode, int64_t Value, + size_t Offset) { + return AllOf(OpcodeIs(Opcode), + ElementsAre(IsReg(llvm::X86::RSP), IsImm(1), IsReg(0), + IsImm(Offset), IsReg(0), IsImm(Value))); +} + +Matcher IsMovValueFromStack(unsigned Opcode, unsigned Reg) { + return AllOf(OpcodeIs(Opcode), + ElementsAre(IsReg(Reg), IsReg(llvm::X86::RSP), IsImm(1), + IsReg(0), IsImm(0), IsReg(0))); +} + +Matcher IsStackAllocate(unsigned Size) { + return AllOf( + OpcodeIs(llvm::X86::SUB64ri8), + ElementsAre(IsReg(llvm::X86::RSP), IsReg(llvm::X86::RSP), IsImm(Size))); +} + +Matcher IsStackDeallocate(unsigned Size) { + return AllOf( + OpcodeIs(llvm::X86::ADD64ri8), + ElementsAre(IsReg(llvm::X86::RSP), IsReg(llvm::X86::RSP), IsImm(Size))); +} + constexpr const char kTriple[] = "x86_64-unknown-linux"; +constexpr const char kFeaturesEmpty[] = ""; +constexpr const char kFeaturesAvx[] = "+avx"; +constexpr const char kFeaturesAvx512VL[] = "+avx512vl"; +constexpr const char kCpuCore2[] = "core2"; +template class X86TargetTest : public ::testing::Test { protected: X86TargetTest() @@ -29,7 +114,9 @@ std::string error; Target_ = llvm::TargetRegistry::lookupTarget(kTriple, error); EXPECT_THAT(Target_, NotNull()); + STI_.reset(Target_->createMCSubtargetInfo(kTriple, kCpuCore2, Features)); } + static void SetUpTestCase() { LLVMInitializeX86TargetInfo(); LLVMInitializeX86Target(); @@ -37,133 +124,208 @@ InitializeX86ExegesisTarget(); } + std::vector setRegTo(unsigned Reg, const APInt &Value) { + return ExegesisTarget_->setRegTo(*STI_, Reg, Value); + } + const llvm::Target *Target_; const ExegesisTarget *const ExegesisTarget_; + std::unique_ptr STI_; }; -TEST_F(X86TargetTest, SetRegToConstantGPR) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, 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_SSE2) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1); - EXPECT_THAT(Insts, SizeIs(7U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOVDQUrm); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantXMM_AVX) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "+avx")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1); - EXPECT_THAT(Insts, SizeIs(7U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::VMOVDQUrm); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantXMM_AVX512) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::XMM1); - EXPECT_THAT(Insts, SizeIs(7U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::VMOVDQU32Z128rm); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantMMX) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::MM1); - EXPECT_THAT(Insts, SizeIs(5U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MMX_MOVQ64rm); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantYMM_AVX) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "+avx")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::YMM1); - EXPECT_THAT(Insts, SizeIs(11U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::VMOVDQUYrm); - EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantYMM_AVX512) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::YMM1); - EXPECT_THAT(Insts, SizeIs(11U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::VMOVDQU32Z256rm); - EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::ADD64ri8); -} - -TEST_F(X86TargetTest, SetRegToConstantZMM_AVX512) { - const std::unique_ptr STI( - Target_->createMCSubtargetInfo(kTriple, "core2", "+avx512vl")); - const auto Insts = ExegesisTarget_->setRegToConstant(*STI, llvm::X86::ZMM1); - EXPECT_THAT(Insts, SizeIs(19U)); - EXPECT_EQ(Insts[0].getOpcode(), llvm::X86::SUB64ri8); - EXPECT_EQ(Insts[1].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[2].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[3].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[4].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[5].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[6].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[7].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[8].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[9].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[10].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[11].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[12].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[13].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[14].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[15].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[16].getOpcode(), llvm::X86::MOV32mi); - EXPECT_EQ(Insts[17].getOpcode(), llvm::X86::VMOVDQU32Zrm); - EXPECT_EQ(Insts[18].getOpcode(), llvm::X86::ADD64ri8); +using Core2TargetTest = X86TargetTest; +using Core2AvxTargetTest = X86TargetTest; +using Core2Avx512TargetTest = X86TargetTest; + +TEST_F(Core2TargetTest, SetFlags) { + const unsigned Reg = llvm::X86::EFLAGS; + EXPECT_THAT( + setRegTo(Reg, APInt(64, 0x1111222233334444ULL)), + ElementsAre(IsStackAllocate(8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 4), + OpcodeIs(llvm::X86::POPF64))); +} + +TEST_F(Core2TargetTest, SetRegToGR8Value) { + const uint8_t Value = 0xFFU; + const unsigned Reg = llvm::X86::AL; + EXPECT_THAT(setRegTo(Reg, APInt(8, Value)), + ElementsAre(IsMovImmediate(llvm::X86::MOV8ri, Reg, Value))); +} + +TEST_F(Core2TargetTest, SetRegToGR16Value) { + const uint16_t Value = 0xFFFFU; + const unsigned Reg = llvm::X86::BX; + EXPECT_THAT(setRegTo(Reg, APInt(16, Value)), + ElementsAre(IsMovImmediate(llvm::X86::MOV16ri, Reg, Value))); +} + +TEST_F(Core2TargetTest, SetRegToGR32Value) { + const uint32_t Value = 0x7FFFFU; + const unsigned Reg = llvm::X86::ECX; + EXPECT_THAT(setRegTo(Reg, APInt(32, Value)), + ElementsAre(IsMovImmediate(llvm::X86::MOV32ri, Reg, Value))); +} + +TEST_F(Core2TargetTest, SetRegToGR64Value) { + const uint64_t Value = 0x7FFFFFFFFFFFFFFFULL; + const unsigned Reg = llvm::X86::RDX; + EXPECT_THAT(setRegTo(Reg, APInt(64, Value)), + ElementsAre(IsMovImmediate(llvm::X86::MOV64ri, Reg, Value))); +} + +TEST_F(Core2TargetTest, SetRegToVR64Value) { + EXPECT_THAT( + setRegTo(llvm::X86::MM0, APInt(64, 0x1111222233334444ULL)), + ElementsAre(IsStackAllocate(8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 4), + IsMovValueFromStack(llvm::X86::MMX_MOVQ64rm, llvm::X86::MM0), + IsStackDeallocate(8))); +} + +TEST_F(Core2TargetTest, SetRegToVR128Value_Use_MOVDQUrm) { + EXPECT_THAT( + setRegTo(llvm::X86::XMM0, + APInt(128, "11112222333344445555666677778888", 16)), + ElementsAre(IsStackAllocate(16), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77778888UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55556666UL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 12), + IsMovValueFromStack(llvm::X86::MOVDQUrm, llvm::X86::XMM0), + IsStackDeallocate(16))); +} + +TEST_F(Core2AvxTargetTest, SetRegToVR128Value_Use_VMOVDQUrm) { + EXPECT_THAT( + setRegTo(llvm::X86::XMM0, + APInt(128, "11112222333344445555666677778888", 16)), + ElementsAre(IsStackAllocate(16), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77778888UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55556666UL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 12), + IsMovValueFromStack(llvm::X86::VMOVDQUrm, llvm::X86::XMM0), + IsStackDeallocate(16))); +} + +TEST_F(Core2Avx512TargetTest, SetRegToVR128Value_Use_VMOVDQU32Z128rm) { + EXPECT_THAT( + setRegTo(llvm::X86::XMM0, + APInt(128, "11112222333344445555666677778888", 16)), + ElementsAre( + IsStackAllocate(16), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77778888UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55556666UL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 12), + IsMovValueFromStack(llvm::X86::VMOVDQU32Z128rm, llvm::X86::XMM0), + IsStackDeallocate(16))); +} + +TEST_F(Core2AvxTargetTest, SetRegToVR256Value_Use_VMOVDQUYrm) { + const char ValueStr[] = + "1111111122222222333333334444444455555555666666667777777788888888"; + EXPECT_THAT(setRegTo(llvm::X86::YMM0, APInt(256, ValueStr, 16)), + ElementsAreArray( + {IsStackAllocate(32), + IsMovValueToStack(llvm::X86::MOV32mi, 0x88888888UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77777777UL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x66666666UL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55555555UL, 12), + IsMovValueToStack(llvm::X86::MOV32mi, 0x44444444UL, 16), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33333333UL, 20), + IsMovValueToStack(llvm::X86::MOV32mi, 0x22222222UL, 24), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11111111UL, 28), + IsMovValueFromStack(llvm::X86::VMOVDQUYrm, llvm::X86::YMM0), + IsStackDeallocate(32)})); +} + +TEST_F(Core2Avx512TargetTest, SetRegToVR256Value_Use_VMOVDQU32Z256rm) { + const char ValueStr[] = + "1111111122222222333333334444444455555555666666667777777788888888"; + EXPECT_THAT( + setRegTo(llvm::X86::YMM0, APInt(256, ValueStr, 16)), + ElementsAreArray( + {IsStackAllocate(32), + IsMovValueToStack(llvm::X86::MOV32mi, 0x88888888UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77777777UL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x66666666UL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55555555UL, 12), + IsMovValueToStack(llvm::X86::MOV32mi, 0x44444444UL, 16), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33333333UL, 20), + IsMovValueToStack(llvm::X86::MOV32mi, 0x22222222UL, 24), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11111111UL, 28), + IsMovValueFromStack(llvm::X86::VMOVDQU32Z256rm, llvm::X86::YMM0), + IsStackDeallocate(32)})); +} + +TEST_F(Core2Avx512TargetTest, SetRegToVR512Value) { + const char ValueStr[] = + "1111111122222222333333334444444455555555666666667777777788888888" + "99999999AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEEEEEEFFFFFFFF00000000"; + EXPECT_THAT( + setRegTo(llvm::X86::ZMM0, APInt(512, ValueStr, 16)), + ElementsAreArray( + {IsStackAllocate(64), + IsMovValueToStack(llvm::X86::MOV32mi, 0x00000000UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0xFFFFFFFFUL, 4), + IsMovValueToStack(llvm::X86::MOV32mi, 0xEEEEEEEEUL, 8), + IsMovValueToStack(llvm::X86::MOV32mi, 0xDDDDDDDDUL, 12), + IsMovValueToStack(llvm::X86::MOV32mi, 0xCCCCCCCCUL, 16), + IsMovValueToStack(llvm::X86::MOV32mi, 0xBBBBBBBBUL, 20), + IsMovValueToStack(llvm::X86::MOV32mi, 0xAAAAAAAAUL, 24), + IsMovValueToStack(llvm::X86::MOV32mi, 0x99999999UL, 28), + IsMovValueToStack(llvm::X86::MOV32mi, 0x88888888UL, 32), + IsMovValueToStack(llvm::X86::MOV32mi, 0x77777777UL, 36), + IsMovValueToStack(llvm::X86::MOV32mi, 0x66666666UL, 40), + IsMovValueToStack(llvm::X86::MOV32mi, 0x55555555UL, 44), + IsMovValueToStack(llvm::X86::MOV32mi, 0x44444444UL, 48), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33333333UL, 52), + IsMovValueToStack(llvm::X86::MOV32mi, 0x22222222UL, 56), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11111111UL, 60), + IsMovValueFromStack(llvm::X86::VMOVDQU32Zrm, llvm::X86::ZMM0), + IsStackDeallocate(64)})); +} + +TEST_F(Core2TargetTest, SetRegToST0_32Bits) { + EXPECT_THAT( + setRegTo(llvm::X86::ST0, APInt(32, 0x11112222ULL)), + ElementsAre(IsStackAllocate(4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 0), + OpcodeIs(llvm::X86::LD_F32m), IsStackDeallocate(4))); +} + +TEST_F(Core2TargetTest, SetRegToST1_32Bits) { + const MCInst CopySt0ToSt1 = + llvm::MCInstBuilder(llvm::X86::ST_Frr).addReg(llvm::X86::ST1); + EXPECT_THAT( + setRegTo(llvm::X86::ST1, APInt(32, 0x11112222ULL)), + ElementsAre(IsStackAllocate(4), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 0), + OpcodeIs(llvm::X86::LD_F32m), CopySt0ToSt1, + IsStackDeallocate(4))); +} + +TEST_F(Core2TargetTest, SetRegToST0_64Bits) { + EXPECT_THAT( + setRegTo(llvm::X86::ST0, APInt(64, 0x1111222233334444ULL)), + ElementsAre(IsStackAllocate(8), + IsMovValueToStack(llvm::X86::MOV32mi, 0x33334444UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x11112222UL, 4), + OpcodeIs(llvm::X86::LD_F64m), IsStackDeallocate(8))); +} + +TEST_F(Core2TargetTest, SetRegToST0_80Bits) { + EXPECT_THAT( + setRegTo(llvm::X86::ST0, APInt(80, "11112222333344445555", 16)), + ElementsAre(IsStackAllocate(10), + IsMovValueToStack(llvm::X86::MOV32mi, 0x44445555UL, 0), + IsMovValueToStack(llvm::X86::MOV32mi, 0x22223333UL, 4), + IsMovValueToStack(llvm::X86::MOV16mi, 0x1111UL, 8), + OpcodeIs(llvm::X86::LD_F80m), IsStackDeallocate(10))); } } // namespace