diff --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h --- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h +++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.h @@ -19,6 +19,8 @@ namespace llvm { namespace exegesis { +enum class RegRandomizationStrategy : uint8_t; + class ParallelSnippetGenerator : public SnippetGenerator { public: using SnippetGenerator::SnippetGenerator; @@ -31,6 +33,10 @@ static constexpr const size_t kMinNumDifferentAddresses = 6; private: + void generateCodeTemplatesImpl(InstructionTemplate Variant, CodeTemplate &CT, + RegRandomizationStrategy Strategy, + const BitVector &ForbiddenRegisters) const; + // Instantiates memory operands within a snippet. // To make computations as parallel as possible, we generate independant // memory locations for instructions that load and store. If there are less diff --git a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp --- a/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp +++ b/llvm/tools/llvm-exegesis/lib/ParallelSnippetGenerator.cpp @@ -80,7 +80,6 @@ namespace exegesis { static bool hasVariablesWithTiedOperands(const Instruction &Instr) { - SmallVector Result; for (const auto &Var : Instr.Variables) if (Var.hasTiedOperands()) return true; @@ -291,6 +290,37 @@ } } +void ParallelSnippetGenerator::generateCodeTemplatesImpl( + InstructionTemplate Variant, CodeTemplate &CT, + RegRandomizationStrategy Strategy, + const BitVector &ForbiddenRegisters) const { + const Instruction &Instr = Variant.getInstr(); + const AliasingConfigurations SelfAliasing(Instr, Instr, ForbiddenRegisters); + if (SelfAliasing.empty()) { + CT.Info = "instruction is parallel, repeating a random one."; + CT.Instructions = generateSnippetForInstrAvoidingDefUseOverlap( + State, Variant, Strategy, ForbiddenRegisters); + return; + } + if (SelfAliasing.hasImplicitAliasing()) { + CT.Info = "instruction is serial, repeating a random one."; + CT.Instructions.push_back(std::move(Variant)); + return; + } + { + bool HasTiedOperands = hasVariablesWithTiedOperands(Instr); + CT.Info = Twine("instruction has ") + .concat(HasTiedOperands ? "" : "no ") + .concat("tied variables, avoiding " + "Read-After-Write issue, picking random def and use " + "registers not aliasing each other.") + .str(); + CT.Instructions = generateSnippetForInstrAvoidingDefUseOverlap( + State, Variant, Strategy, ForbiddenRegisters); + return; + } +} + Expected> ParallelSnippetGenerator::generateCodeTemplates( InstructionTemplate Variant, const BitVector &ForbiddenRegisters) const { @@ -301,54 +331,39 @@ ? State.getExegesisTarget().getScratchMemoryRegister( State.getTargetMachine().getTargetTriple()) : 0; - const AliasingConfigurations SelfAliasing(Instr, Instr, ForbiddenRegisters); - if (SelfAliasing.empty()) { - CT.Info = "instruction is parallel, repeating a random one."; - CT.Instructions.push_back(std::move(Variant)); - instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); - return getSingleton(std::move(CT)); - } - if (SelfAliasing.hasImplicitAliasing()) { - CT.Info = "instruction is serial, repeating a random one."; - CT.Instructions.push_back(std::move(Variant)); - instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); - return getSingleton(std::move(CT)); - } - std::vector Result; + bool HasTiedOperands = hasVariablesWithTiedOperands(Instr); // If there are no tied operands, then we don't want to "saturate backedge", // and the template we will produce will have only a single instruction. + SmallVector Strategies; unsigned NumUntiedUseRegs = count_if(Instr.Operands, [](const Operand &Op) { return Op.isReg() && Op.isExplicit() && !Op.isMemory() && Op.isUse() && !Op.isTied(); }); - SmallVector Strategies; if (HasTiedOperands || NumUntiedUseRegs >= 3) Strategies.push_back(RegRandomizationStrategy::PickRandomRegs); if (NumUntiedUseRegs >= 2) Strategies.push_back(RegRandomizationStrategy::SingleStaticRegPerOperand); Strategies.push_back(RegRandomizationStrategy::SingleStaticReg); - for (RegRandomizationStrategy S : Strategies) { - CodeTemplate CurrCT = CT.clone(); - CurrCT.Info = - Twine("instruction has ") - .concat(HasTiedOperands ? "" : "no ") - .concat("tied variables, avoiding " - "Read-After-Write issue, picking random def and use " - "registers not aliasing each other, for uses, ") - .concat(getDescription(S)) - .str(); - CurrCT.Instructions = generateSnippetForInstrAvoidingDefUseOverlap( - State, Variant, S, ForbiddenRegisters); + + std::vector Results; + Results.reserve(Strategies.size()); + for (RegRandomizationStrategy Strategy : Strategies) { + CodeTemplate CurrCT = CT.clone(); // FIXME: avoid clone for last strategy? + generateCodeTemplatesImpl(Variant, CurrCT, Strategy, ForbiddenRegisters); + CurrCT.Info = Twine(CurrCT.Info) + .concat(" For uses, ") + .concat(getDescription(Strategy)) + .str(); if (CurrCT.Instructions.empty()) - return make_error( - Twine("Failed to produce any snippet via: ").concat(CurrCT.Info), - inconvertibleErrorCode()); + continue; instantiateMemoryOperands(CurrCT.ScratchSpacePointerInReg, CurrCT.Instructions); - Result.push_back(std::move(CurrCT)); + Results.push_back(std::move(CurrCT)); } - return Result; + if (Results.empty()) + return make_error("No strategy found to produce parallel snippet"); + return std::move(Results); } constexpr const size_t ParallelSnippetGenerator::kMinNumDifferentAddresses; diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -314,6 +314,10 @@ static Expected> generateSnippets(const LLVMState &State, unsigned Opcode, const BitVector &ForbiddenRegs) { + PrettyStackTraceFormat StackTrace("Generating snippets for opcode %u (%s)", + Opcode, + State.getInstrInfo().getName(Opcode)); + const Instruction &Instr = State.getIC().getInstr(Opcode); const MCInstrDesc &InstrDesc = Instr.Description; // Ignore instructions that we cannot run. diff --git a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp --- a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -426,8 +426,9 @@ auto Err = Generator.generateConfigurations(&Instr, Benchmarks, State.getRATC().emptyRegisters()); EXPECT_TRUE((bool)Err); - EXPECT_THAT(toString(std::move(Err)), - testing::HasSubstr("no available registers")); + EXPECT_THAT( + toString(std::move(Err)), + testing::HasSubstr("No strategy found to produce parallel snippet")); } TEST_F(X86ParallelSnippetGeneratorTest, @@ -449,8 +450,54 @@ AllRegisters.reset(X86::DL); auto Err = Generator.generateCodeTemplates(&Instr, AllRegisters); EXPECT_FALSE((bool)Err); - EXPECT_THAT(toString(Err.takeError()), - testing::HasSubstr("Failed to produce any snippet")); + EXPECT_THAT( + toString(Err.takeError()), + testing::HasSubstr("No strategy found to produce parallel snippet")); +} + +TEST_F(X86ParallelSnippetGeneratorTest, CMP64rr_idiom) { + // - CMP64rr + // - Op0 Explicit Use RegClass(GR64) + // - Op1 Explicit Use RegClass(GR64) + // - Op2 Implicit Def Reg(EFLAGS) + // - Var0 [Op0] + // - Var1 [Op1] + const unsigned Opcode = X86::CMP64rr; + const Instruction &Instr = State.getIC().getInstr(Opcode); + auto ForbiddenRegisters = State.getRATC().emptyRegisters(); + ForbiddenRegisters.flip(); + ForbiddenRegisters.reset(X86::RAX); + ForbiddenRegisters.reset(X86::RBX); + auto Error = Generator.generateCodeTemplates(&Instr, ForbiddenRegisters); + EXPECT_FALSE((bool)Error.takeError()); + auto CodeTemplates = std::move(Error.get()); + ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available"; + for (const auto &CT : CodeTemplates) { + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionTemplate &IT = CT.Instructions[0]; + EXPECT_THAT(IT.getOpcode(), Opcode); + ASSERT_THAT(IT.getVariableValues(), SizeIs(2)); + EXPECT_THAT(IT.getVariableValues(), ElementsAre(IsReg(), IsReg())); + } + { + const auto &CT = CodeTemplates[0]; + EXPECT_THAT( + CT.Info, + testing::HasSubstr("instruction is parallel, repeating a random one. " + "For uses, one unique register for each position")); + ArrayRef VariableValues = CT.Instructions[0].getVariableValues(); + ASSERT_NE(VariableValues[0].getReg(), VariableValues[1].getReg()); + } + { + const auto &CT = CodeTemplates[1]; + EXPECT_THAT(CT.Info, + testing::HasSubstr( + "instruction is parallel, repeating a random one. For " + "uses, reusing the same register for all positions")); + ArrayRef VariableValues = CT.Instructions[0].getVariableValues(); + ASSERT_EQ(VariableValues[0].getReg(), VariableValues[1].getReg()); + } } class X86FakeSnippetGenerator : public SnippetGenerator {