From e0587165c6449f37a23eeb2e3ac4ecd22ed2acd1 Mon Sep 17 00:00:00 2001 From: Anastasiya Chernikova Date: Thu, 4 Apr 2024 14:52:53 +0300 Subject: [PATCH] [Exegesis][RISCV] Add RISCV support for llvm-exegesis Llvm-exegesis RISCV port is a result of team effort. Below everyone involved listed. Co-authored-by: Konstantin Vladimirov Co-authored-by: Dmitrii Petrov Co-authored-by: Dmitry Bushev Co-authored-by: Mark Goncharov Co-authored-by: Anastasiya Chernikova --- .../Target/RISCV/MCTargetDesc/RISCVBaseInfo.h | 3 + llvm/lib/Target/RISCV/RISCVInstrInfo.td | 4 +- llvm/lib/Target/RISCV/RISCVInstrInfoF.td | 4 + .../RISCV/latency-by-extension-A.s | 59 +++ .../RISCV/latency-by-extension-C.s | 65 +++ .../llvm-exegesis/RISCV/latency-by-load.s | 60 +++ .../RISCV/latency-by-opcode-name-FADD_D.s | 11 + .../RISCV/latency-pre-assigned-register.s | 3 + .../llvm-exegesis/lib/AArch64/Target.cpp | 4 + llvm/tools/llvm-exegesis/lib/Assembler.cpp | 10 +- llvm/tools/llvm-exegesis/lib/BenchmarkCode.h | 5 + llvm/tools/llvm-exegesis/lib/CMakeLists.txt | 3 + llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp | 8 + llvm/tools/llvm-exegesis/lib/CodeTemplate.h | 6 + llvm/tools/llvm-exegesis/lib/LlvmState.cpp | 6 + .../llvm-exegesis/lib/MCInstrDescView.cpp | 33 +- .../tools/llvm-exegesis/lib/MCInstrDescView.h | 13 +- llvm/tools/llvm-exegesis/lib/Mips/Target.cpp | 6 +- llvm/tools/llvm-exegesis/lib/PerfHelper.h | 2 +- .../llvm-exegesis/lib/PowerPC/Target.cpp | 6 + .../llvm-exegesis/lib/RISCV/CMakeLists.txt | 22 + .../llvm-exegesis/lib/RISCV/RISCVCounters.cpp | 66 +++ .../llvm-exegesis/lib/RISCV/RISCVCounters.h | 31 ++ llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp | 409 ++++++++++++++++++ .../lib/SerialSnippetGenerator.cpp | 62 ++- llvm/tools/llvm-exegesis/lib/SnippetFile.cpp | 20 +- .../llvm-exegesis/lib/SnippetGenerator.cpp | 13 +- llvm/tools/llvm-exegesis/lib/Target.cpp | 4 + llvm/tools/llvm-exegesis/lib/Target.h | 19 + llvm/tools/llvm-exegesis/lib/X86/Target.cpp | 6 + llvm/tools/llvm-exegesis/llvm-exegesis.cpp | 68 ++- 31 files changed, 983 insertions(+), 48 deletions(-) create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/latency-by-load.s create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s create mode 100644 llvm/test/tools/llvm-exegesis/RISCV/latency-pre-assigned-register.s create mode 100644 llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt create mode 100644 llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.cpp create mode 100644 llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.h create mode 100644 llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h index 92f405b5f6acbf..9e83add7c4c3d9 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -299,6 +299,9 @@ enum OperandType : unsigned { OPERAND_RVKRNUM_2_14, OPERAND_SPIMM, OPERAND_LAST_RISCV_IMM = OPERAND_SPIMM, + // Operand is a 3-bit rounding mode, '111' indicates FRM register. + // Represents 'frm' argument passing to floating-point operations. + OPERAND_FRMARG, // Operand is either a register or uimm5, this is used by V extension pseudo // instructions to represent a value that be passed as AVL to either vsetvli // or vsetivli. diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index f9dadc6c0d4895..dddbeeddffa5b4 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -509,7 +509,7 @@ class BranchCC_rri funct3, string opcodestr> let isTerminator = 1; } -let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in { +let hasSideEffects = 0, mayLoad = 1, mayStore = 0, UseNamedOperandTable = 1 in { class Load_ri funct3, string opcodestr> : RVInstI; @@ -524,7 +524,7 @@ class HLoad_r funct7, bits<5> funct5, string opcodestr> // Operands for stores are in the order srcreg, base, offset rather than // reflecting the order these fields are specified in the instruction // encoding. -let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in { +let hasSideEffects = 0, mayLoad = 0, mayStore = 1, UseNamedOperandTable = 1 in { class Store_rri funct3, string opcodestr> : RVInstS { let ParserMatchClass = FRMArg; let PrintMethod = "printFRMArg"; let DecoderMethod = "decodeFRMArg"; + let OperandType = "OPERAND_FRMARG"; + let OperandNamespace = "RISCVOp"; } // Variants of the rounding mode operand that default to 'rne'. This is used @@ -150,6 +152,8 @@ def frmarglegacy : Operand { let ParserMatchClass = FRMArgLegacy; let PrintMethod = "printFRMArgLegacy"; let DecoderMethod = "decodeFRMArg"; + let OperandType = "OPERAND_FRMARG"; + let OperandNamespace = "RISCVOp"; } //===----------------------------------------------------------------------===// diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s new file mode 100644 index 00000000000000..4b1ec2756bac10 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-A.s @@ -0,0 +1,59 @@ +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=AMOAND_D -mattr="+a" |& FileCheck --check-prefix=TEST1 %s + +TEST1: --- +TEST1-NEXT: mode: latency +TEST1-NEXT: key: +TEST1-NEXT: instructions: +TEST1-NEXT: - 'AMOAND_D [[RE01:X[0-9]+]] X10 [[RE01:X[0-9]+]]' +TEST1-NEXT: config: '' +TEST1-NEXT: register_initial_values: +TEST1-NEXT: - '[[RE01:X[0-9]+]]=0x0' +TEST1-LAST: ... + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=AMOADD_W -mattr="+a" |& FileCheck --check-prefix=TEST2 %s + +TEST2: --- +TEST2-NEXT: mode: latency +TEST2-NEXT: key: +TEST2-NEXT: instructions: +TEST2-NEXT: - 'AMOADD_W [[RE02:X[0-9]+]] X10 [[RE02:X[0-9]+]]' +TEST2-NEXT: config: '' +TEST2-NEXT: register_initial_values: +TEST2-NEXT: - '[[RE02:X[0-9]+]]=0x0' +TEST2-LAST: ... + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=AMOMAXU_D -mattr="+a" |& FileCheck --check-prefix=TEST3 %s + +TEST3: --- +TEST3-NEXT: mode: latency +TEST3-NEXT: key: +TEST3-NEXT: instructions: +TEST3-NEXT: - 'AMOMAXU_D [[RE03:X[0-9]+]] X10 [[RE03:X[0-9]+]]' +TEST3-NEXT: config: '' +TEST3-NEXT: register_initial_values: +TEST3-NEXT: - '[[RE03:X[0-9]+]]=0x0' +TEST3-LAST: ... + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=AMOMIN_W -mattr="+a" |& FileCheck --check-prefix=TEST4 %s + +TEST4: --- +TEST4-NEXT: mode: latency +TEST4-NEXT: key: +TEST4-NEXT: instructions: +TEST4-NEXT: - 'AMOMIN_W [[RE04:X[0-9]+]] X10 [[RE04:X[0-9]+]]' +TEST4-NEXT: config: '' +TEST4-NEXT: register_initial_values: +TEST4-NEXT: - '[[RE04:X[0-9]+]]=0x0' +TEST4-LAST: ... + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=AMOXOR_D -mattr="+a" |& FileCheck --check-prefix=TEST5 %s + +TEST5: --- +TEST5-NEXT: mode: latency +TEST5-NEXT: key: +TEST5-NEXT: instructions: +TEST5-NEXT: - 'AMOXOR_D [[RE05:X[0-9]+]] X10 [[RE05:X[0-9]+]]' +TEST5-NEXT: config: '' +TEST5-NEXT: register_initial_values: +TEST5-NEXT: - '[[RE05:X[0-9]+]]=0x0' +TEST5-LAST: ... diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s new file mode 100644 index 00000000000000..994e2a4b072114 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-extension-C.s @@ -0,0 +1,65 @@ +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_ADDI -mattr=+c |& FileCheck --check-prefix=TEST1 %s + +TEST1: --- +TEST1-NEXT: mode: latency +TEST1-NEXT: key: +TEST1-NEXT: instructions: +TEST1-NEXT: - 'C_ADDI [[REG01:X[0-9]+]] [[RE02:X[0-9]+]] [[IMM0:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_ADDIW -mattr=+c |& FileCheck --check-prefix=TEST2 %s + +TEST2: --- +TEST2-NEXT: mode: latency +TEST2-NEXT: key: +TEST2-NEXT: instructions: +TEST2-NEXT: - 'C_ADDIW [[REG11:X[0-9]+]] [[RE12:X[0-9]+]] [[IMM1:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_ANDI -mattr=+c |& FileCheck --check-prefix=TEST3 %s + +TEST3: --- +TEST3-NEXT: mode: latency +TEST3-NEXT: key: +TEST3-NEXT: instructions: +TEST3-NEXT: - 'C_ANDI [[REG31:X[0-9]+]] [[REG32:X[0-9]+]] [[IMM3:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_SLLI -mattr=+c |& FileCheck --check-prefix=TEST4 %s + +TEST4: --- +TEST4-NEXT: mode: latency +TEST4-NEXT: key: +TEST4-NEXT: instructions: +TEST4-NEXT: - 'C_SLLI [[REG81:X[0-9]+]] [[REG82:X[0-9]+]] [[IMM8:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_SRAI -mattr=+c |& FileCheck --check-prefix=TEST5 %s + +TEST5: --- +TEST5-NEXT: mode: latency +TEST5-NEXT: key: +TEST5-NEXT: instructions: +TEST5-NEXT: - 'C_SRAI [[REG91:X[0-9]+]] [[REG92:X[0-9]+]] [[IMM9:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_SRLI -mattr=+c |& FileCheck --check-prefix=TEST6 %s + +TEST6: --- +TEST6-NEXT: mode: latency +TEST6-NEXT: key: +TEST6-NEXT: instructions: +TEST6-NEXT: - 'C_SRLI [[REG101:X[0-9]+]] [[REG102:X[0-9]+]] [[IMM10:i_0x[0-9]+]]' +TEST6-LAST: ... + + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_LD -mattr=+c |& FileCheck --check-prefix=TEST7 %s + +TEST7: --- +TEST7-NEXT: mode: latency +TEST7-NEXT: key: +TEST7-NEXT: instructions: +TEST7-NEXT: - 'C_LD [[REG61:X[0-9]+]] [[REG62:X[0-9]+]] [[IMM6:i_0x[0-9]+]]' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=C_LW -mattr=+c |& FileCheck --check-prefix=TEST8 %s + +TEST8: --- +TEST8-NEXT: mode: latency +TEST8-NEXT: key: +TEST8-NEXT: instructions: +TEST8-NEXT: - 'C_LW [[REG71:X[0-9]+]] [[REG72:X[0-9]+]] [[IMM7:i_0x[0-9]+]]' diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-load.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-load.s new file mode 100644 index 00000000000000..6950d90bd43bee --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-load.s @@ -0,0 +1,60 @@ +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LD |& FileCheck --check-prefix=TEST1 %s + +TEST1: --- +TEST1-NEXT: mode: latency +TEST1-NEXT: key: +TEST1-NEXT: instructions: +TEST1-NEXT: - 'LD X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LW |& FileCheck --check-prefix=TEST2 %s + +TEST2: --- +TEST2-NEXT: mode: latency +TEST2-NEXT: key: +TEST2-NEXT: instructions: +TEST2-NEXT: - 'LW X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LH |& FileCheck --check-prefix=TEST3 %s + +TEST3: --- +TEST3-NEXT: mode: latency +TEST3-NEXT: key: +TEST3-NEXT: instructions: +TEST3-NEXT: - 'LH X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LWU |& FileCheck --check-prefix=TEST4 %s + +TEST4: --- +TEST4-NEXT: mode: latency +TEST4-NEXT: key: +TEST4-NEXT: instructions: +TEST4-NEXT: - 'LWU X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LBU |& FileCheck --check-prefix=TEST5 %s + +TEST5: --- +TEST5-NEXT: mode: latency +TEST5-NEXT: key: +TEST5-NEXT: instructions: +TEST5-NEXT: - 'LBU X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LUI |& FileCheck --check-prefix=TEST6 %s + +TEST6: LUI: No strategy found to make the execution serial + + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LB |& FileCheck --check-prefix=TEST7 %s + +TEST7: --- +TEST7-NEXT: mode: latency +TEST7-NEXT: key: +TEST7-NEXT: instructions: +TEST7-NEXT: - 'LB X10 X10 i_0x0' + +# RUN: llvm-exegesis -mode=latency -mtriple=riscv64-unknown-linux-gnu --benchmark-phase=assemble-measured-code -opcode-name=LR_W_RL -mattr="+a" |& FileCheck --check-prefix=TEST8 %s + +TEST8: --- +TEST8-NEXT: mode: latency +TEST8-NEXT: key: +TEST8-NEXT: instructions: +TEST8-NEXT: - 'LR_W_RL X10 X10' diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s new file mode 100644 index 00000000000000..b2e2be5253d936 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-by-opcode-name-FADD_D.s @@ -0,0 +1,11 @@ +# RUN: llvm-exegesis -mtriple=riscv64-unknown-linux-gnu -mode=latency --benchmark-phase=assemble-measured-code -mattr=+d -opcode-name=FADD_D |& FileCheck %s + +CHECK: --- +CHECK-NEXT: mode: latency +CHECK-NEXT: key: +CHECK-NEXT: instructions: +CHECK-NEXT: - 'FADD_D [[REG1:F[0-9]+_D]] [[REG2:F[0-9]+_D]] [[REG3:F[0-9]+_D]] i_0x7' +CHECK-NEXT: config: '' +CHECK-NEXT: register_initial_values: +CHECK-DAG: - '[[REG1]]=0x0' +CHECK-LAST: ... diff --git a/llvm/test/tools/llvm-exegesis/RISCV/latency-pre-assigned-register.s b/llvm/test/tools/llvm-exegesis/RISCV/latency-pre-assigned-register.s new file mode 100644 index 00000000000000..c7453a3bdb095c --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/RISCV/latency-pre-assigned-register.s @@ -0,0 +1,3 @@ +RUN: llvm-exegesis -mode=latency --benchmark-phase=assemble-measured-code -opcode-name=LB -mtriple=riscv64-unknown-linux-gnu + +CHECK: Warning: Pre-assigned register prevented usage of self-aliasing strategy. diff --git a/llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp b/llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp index 51846862f0a734..6a3eba2459556d 100644 --- a/llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/AArch64/Target.cpp @@ -45,6 +45,10 @@ class ExegesisAArch64Target : public ExegesisTarget { : ExegesisTarget(AArch64CpuPfmCounters, AArch64_MC::isOpcodeAvailable) {} private: + unsigned findRegisterByName(const StringRef RegName) const override { + return AArch64::NoRegister; + } + std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const override { if (AArch64::GPR32RegClass.contains(Reg)) diff --git a/llvm/tools/llvm-exegesis/lib/Assembler.cpp b/llvm/tools/llvm-exegesis/lib/Assembler.cpp index 92ab3a96d91e6b..2908ea80d0e471 100644 --- a/llvm/tools/llvm-exegesis/lib/Assembler.cpp +++ b/llvm/tools/llvm-exegesis/lib/Assembler.cpp @@ -168,7 +168,15 @@ void BasicBlockFiller::addInstruction(const MCInst &Inst, const DebugLoc &DL) { } else if (Op.isImm()) { Builder.addImm(Op.getImm()); } else if (!Op.isValid()) { - llvm_unreachable("Operand is not set"); + std::string Message; + llvm::raw_string_ostream MessageOut(Message); + MessageOut << "Operand is not set: Instr: "; + Inst.dump_pretty(MessageOut, MCII->getName(Inst.getOpcode())); + MessageOut << "; OpIndex: " << OpIndex; + const MCOperandInfo &OperandInfo = MCID.operands()[OpIndex]; + MessageOut << "; OpInfo.OperandType: " + << static_cast(OperandInfo.OperandType); + report_fatal_error(Twine(Message)); } else { llvm_unreachable("Not yet implemented"); } diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h b/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h index 1db8472e99f7c9..4d5fc241fde0d9 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkCode.h @@ -17,6 +17,11 @@ namespace llvm { namespace exegesis { +struct ScratchMemoryStore { + unsigned Reg; + unsigned Offset; +}; + // A collection of instructions that are to be assembled, executed and measured. struct BenchmarkCode { BenchmarkKey Key; diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt index 414b49e5e021c2..d95c37ff5426bd 100644 --- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt @@ -12,6 +12,9 @@ endif() if (LLVM_TARGETS_TO_BUILD MATCHES "Mips") list(APPEND LLVM_EXEGESIS_TARGETS "Mips") endif() +if(LLVM_TARGETS_TO_BUILD MATCHES "RISCV") + list(APPEND LLVM_EXEGESIS_TARGETS "RISCV") +endif() set(LLVM_EXEGESIS_TARGETS ${LLVM_EXEGESIS_TARGETS} PARENT_SCOPE) diff --git a/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp b/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp index fd156ee01e7ce5..9b5bfe3196dcda 100644 --- a/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp +++ b/llvm/tools/llvm-exegesis/lib/CodeTemplate.cpp @@ -57,6 +57,14 @@ const MCOperand &InstructionTemplate::getValueFor(const Operand &Op) const { return getValueFor(Instr->Variables[Op.getVariableIndex()]); } +MCOperand &InstructionTemplate::getValueFor(unsigned OpIdx) { + return getValueFor(Instr->Variables[OpIdx]); +} + +const MCOperand &InstructionTemplate::getValueFor(unsigned OpIdx) const { + return getValueFor(Instr->Variables[OpIdx]); +} + bool InstructionTemplate::hasImmediateVariables() const { return any_of(Instr->Variables, [this](const Variable &Var) { return Instr->getPrimaryOperand(Var).isImmediate(); diff --git a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h index 7aca224302a1ff..85fcb4e908f017 100644 --- a/llvm/tools/llvm-exegesis/lib/CodeTemplate.h +++ b/llvm/tools/llvm-exegesis/lib/CodeTemplate.h @@ -35,6 +35,8 @@ struct InstructionTemplate { const MCOperand &getValueFor(const Variable &Var) const; MCOperand &getValueFor(const Operand &Op); const MCOperand &getValueFor(const Operand &Op) const; + MCOperand &getValueFor(unsigned OpIdx); + const MCOperand &getValueFor(unsigned OpIdx) const; bool hasImmediateVariables() const; const Instruction &getInstr() const { return *Instr; } ArrayRef getVariableValues() const { return VariableValues; } @@ -133,6 +135,10 @@ struct CodeTemplate { // the pointer to this memory is passed in to the function. unsigned ScratchSpacePointerInReg = 0; + // Require to pre-store value of a given register (fisrt) + // to scratch memory with given offset (second) + SmallVector, 2> PreinitScratchMemory; + #if defined(__GNUC__) && (defined(__clang__) || LLVM_GNUC_PREREQ(8, 0, 0)) // FIXME: GCC7 bug workaround. Drop #if after GCC7 no longer supported. private: diff --git a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp index 17d09a1ec0cf34..d387251a687e6a 100644 --- a/llvm/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/llvm/tools/llvm-exegesis/lib/LlvmState.cpp @@ -45,6 +45,12 @@ Expected LLVMState::Create(std::string TripleName, if (CpuName == "native") CpuName = std::string(sys::getHostCPUName()); + if (CpuName.empty()) { + std::unique_ptr Empty_STI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + CpuName = Empty_STI->getAllProcessorDescriptions().begin()->Key; + } + std::unique_ptr STI( TheTarget->createMCSubtargetInfo(TripleName, CpuName, "")); assert(STI && "Unable to create subtarget info!"); diff --git a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp index 9c926d1fc61124..7705d9af4057e8 100644 --- a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -89,17 +89,17 @@ const BitVector *BitVectorCache::getUnique(BitVector &&BV) const { return Entry.get(); } -Instruction::Instruction(const MCInstrDesc *Description, StringRef Name, - SmallVector Operands, - SmallVector Variables, - const BitVector *ImplDefRegs, - const BitVector *ImplUseRegs, - const BitVector *AllDefRegs, - const BitVector *AllUseRegs) +Instruction::Instruction( + const MCInstrDesc *Description, StringRef Name, + SmallVector Operands, SmallVector Variables, + const BitVector *ImplDefRegs, const BitVector *ImplUseRegs, + const BitVector *AllDefRegs, const BitVector *AllUseRegs, + const BitVector *MemoryRegs, const BitVector *NotMemoryRegs) : Description(*Description), Name(Name), Operands(std::move(Operands)), Variables(std::move(Variables)), ImplDefRegs(*ImplDefRegs), ImplUseRegs(*ImplUseRegs), AllDefRegs(*AllDefRegs), - AllUseRegs(*AllUseRegs) {} + AllUseRegs(*AllUseRegs), MemoryRegs(*MemoryRegs), + NotMemoryRegs(*NotMemoryRegs) {} std::unique_ptr Instruction::create(const MCInstrInfo &InstrInfo, @@ -166,6 +166,9 @@ Instruction::create(const MCInstrInfo &InstrInfo, BitVector ImplUseRegs = RATC.emptyRegisters(); BitVector AllDefRegs = RATC.emptyRegisters(); BitVector AllUseRegs = RATC.emptyRegisters(); + BitVector MemoryRegs = RATC.emptyRegisters(); + BitVector NotMemoryRegs = RATC.emptyRegisters(); + for (const auto &Op : Operands) { if (Op.isReg()) { const auto &AliasingBits = Op.getRegisterAliasing().aliasedBits(); @@ -177,6 +180,10 @@ Instruction::create(const MCInstrInfo &InstrInfo, ImplDefRegs |= AliasingBits; if (Op.isUse() && Op.isImplicit()) ImplUseRegs |= AliasingBits; + if (Op.isUse() && Op.isMemory()) + MemoryRegs |= AliasingBits; + if (Op.isUse() && !Op.isMemory()) + NotMemoryRegs |= AliasingBits; } } // Can't use make_unique because constructor is private. @@ -185,7 +192,9 @@ Instruction::create(const MCInstrInfo &InstrInfo, std::move(Variables), BVC.getUnique(std::move(ImplDefRegs)), BVC.getUnique(std::move(ImplUseRegs)), BVC.getUnique(std::move(AllDefRegs)), - BVC.getUnique(std::move(AllUseRegs)))); + BVC.getUnique(std::move(AllUseRegs)), + BVC.getUnique(std::move(MemoryRegs)), + BVC.getUnique(std::move(NotMemoryRegs)))); } const Operand &Instruction::getPrimaryOperand(const Variable &Var) const { @@ -240,6 +249,12 @@ bool Instruction::hasAliasingRegisters( ForbiddenRegisters); } +bool Instruction::hasAliasingNotMemoryRegisters( + const BitVector &ForbiddenRegisters) const { + return anyCommonExcludingForbidden(AllDefRegs, NotMemoryRegs, + ForbiddenRegisters); +} + bool Instruction::hasOneUseOrOneDef() const { return AllDefRegs.count() || AllUseRegs.count(); } diff --git a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h index f8ebc07d01f35e..edc1a341b27cee 100644 --- a/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h +++ b/llvm/tools/llvm-exegesis/lib/MCInstrDescView.h @@ -133,6 +133,12 @@ struct Instruction { // aliasing Use and Def registers. bool hasAliasingRegisters(const BitVector &ForbiddenRegisters) const; + // Whether this instruction is self aliasing through some registers. + // Repeating this instruction may execute sequentially by picking aliasing + // Def and Not Memory Use registers. It may also execute in parallel by + // picking non aliasing Def and Not Memory Use registers. + bool hasAliasingNotMemoryRegisters(const BitVector &ForbiddenRegisters) const; + // Whether this instruction's registers alias with OtherInstr's registers. bool hasAliasingRegistersThrough(const Instruction &OtherInstr, const BitVector &ForbiddenRegisters) const; @@ -160,12 +166,17 @@ struct Instruction { const BitVector &ImplUseRegs; // The set of aliased implicit use registers. const BitVector &AllDefRegs; // The set of all aliased def registers. const BitVector &AllUseRegs; // The set of all aliased use registers. + const BitVector &MemoryRegs; // The set of all aliased memory use registers. + const BitVector + &NotMemoryRegs; // The set of all aliased not memory use registers. + private: Instruction(const MCInstrDesc *Description, StringRef Name, SmallVector Operands, SmallVector Variables, const BitVector *ImplDefRegs, const BitVector *ImplUseRegs, const BitVector *AllDefRegs, - const BitVector *AllUseRegs); + const BitVector *AllUseRegs, const BitVector *MemoryRegs, + const BitVector *NotMemoryRegs); }; // Instructions are expensive to instantiate. This class provides a cache of diff --git a/llvm/tools/llvm-exegesis/lib/Mips/Target.cpp b/llvm/tools/llvm-exegesis/lib/Mips/Target.cpp index 731e037c240df0..5084ce54ae23ce 100644 --- a/llvm/tools/llvm-exegesis/lib/Mips/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/Mips/Target.cpp @@ -62,7 +62,7 @@ class ExegesisMipsTarget : public ExegesisTarget { unsigned getMaxMemoryAccessSize() const override { return 64; } void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg, unsigned Offset) const override; - + unsigned findRegisterByName(const StringRef RegName) const override; std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const override; bool matchesArch(Triple::ArchType Arch) const override { @@ -148,6 +148,10 @@ void ExegesisMipsTarget::fillMemoryOperands(InstructionTemplate &IT, setMemOp(IT, 2, MCOperand::createImm(Offset)); // Disp } +unsigned findRegisterByName(const StringRef RegName) const { + return Mips::NoRegister; +} + std::vector ExegesisMipsTarget::setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const { diff --git a/llvm/tools/llvm-exegesis/lib/PerfHelper.h b/llvm/tools/llvm-exegesis/lib/PerfHelper.h index 4a825b293b7169..1e0fca73808630 100644 --- a/llvm/tools/llvm-exegesis/lib/PerfHelper.h +++ b/llvm/tools/llvm-exegesis/lib/PerfHelper.h @@ -119,7 +119,7 @@ class CounterGroup { virtual void start(); /// Stops the measurement of the event. - void stop(); + virtual void stop(); /// Returns the current value of the counter or error if it cannot be read. /// FunctionBytes: The benchmark function being executed. diff --git a/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp b/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp index 5c944c90384e3e..1da8b5a77a83cb 100644 --- a/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/PowerPC/Target.cpp @@ -33,6 +33,8 @@ class ExegesisPowerPCTarget : public ExegesisTarget { : ExegesisTarget(PPCCpuPfmCounters, PPC_MC::isOpcodeAvailable) {} private: + unsigned findRegisterByName(const StringRef RegName) const override; + std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const override; bool matchesArch(Triple::ArchType Arch) const override { @@ -92,6 +94,10 @@ void ExegesisPowerPCTarget::fillMemoryOperands(InstructionTemplate &IT, setMemOp(IT, MemOpIdx + 2, MCOperand::createReg(Reg)); // BaseReg } +unsigned findRegisterByName(const StringRef RegName) const { + return PowerPC::NoRegister; +} + std::vector ExegesisPowerPCTarget::setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const { diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt new file mode 100644 index 00000000000000..ea0ced1786a0cc --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/RISCV/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV + ${LLVM_BINARY_DIR}/lib/Target/RISCV +) + +set(LLVM_LINK_COMPONENTS + RISCV + Exegesis + Core + Support + ) + +add_llvm_library(LLVMExegesisRISCV + DISABLE_LLVM_LINK_LLVM_DYLIB + STATIC + Target.cpp + RISCVCounters.cpp + + DEPENDS + intrinsics_gen + RISCVCommonTableGen + ) diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.cpp b/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.cpp new file mode 100644 index 00000000000000..1d421b3c31c36e --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.cpp @@ -0,0 +1,66 @@ +//===-- RISCVCounters.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RISCVCounters.h" + +namespace llvm { +namespace exegesis { + +// This implementation of RISCV target for Exegesis doesn't use libpfm +// and provides manual implementation of performance counters. + +inline uint64_t getRISCVCpuCyclesCount() { +#ifdef __riscv + uint64_t Counter; + asm("csrr %0, cycle" : "=r"(Counter)::"memory"); + return Counter; +#else + return 0; +#endif +} + +class RISCVCpuCyclesCounter : public pfm::CounterGroup { + uint64_t StartValue; + uint64_t EndValue; + uint64_t MeasurementCycles; + +public: + explicit RISCVCpuCyclesCounter(pfm::PerfEvent &&Event); + + void start() override { StartValue = getRISCVCpuCyclesCount(); } + + void stop() override { EndValue = getRISCVCpuCyclesCount(); } + + Expected> + readOrError(StringRef FunctionBytes) const override; +}; + +RISCVCpuCyclesCounter::RISCVCpuCyclesCounter(pfm::PerfEvent &&Event) + : CounterGroup(std::move(Event), {}) { + StartValue = getRISCVCpuCyclesCount(); + EndValue = getRISCVCpuCyclesCount(); + MeasurementCycles = EndValue - StartValue; + if (MeasurementCycles == 0) { + report_fatal_error("MeasurementCycles == 0, " + "performance counters are not configured."); + } + StartValue = EndValue = 0; +} + +Expected> +RISCVCpuCyclesCounter::readOrError(StringRef FunctionBytes) const { + uint64_t Counter = EndValue - StartValue - MeasurementCycles; + return SmallVector({static_cast(Counter)}); +} +std::unique_ptr +createRISCVCpuCyclesCounter(pfm::PerfEvent &&Event) { + return std::make_unique(std::move(Event)); +} + +} // namespace exegesis +} // namespace llvm diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.h b/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.h new file mode 100644 index 00000000000000..203f6af76aaaf8 --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/RISCV/RISCVCounters.h @@ -0,0 +1,31 @@ +//===-- RISCVCounters.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// RISC-V perf counters. +/// +/// More info at: https://lwn.net/Articles/680985 +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LIB_RISCV_RISCVCOUNTERS_H +#define LLVM_TOOLS_LLVM_EXEGESIS_LIB_RISCV_RISCVCOUNTERS_H + +#include "../PerfHelper.h" +#include "../Target.h" +#include + +namespace llvm { +namespace exegesis { + +std::unique_ptr +createRISCVCpuCyclesCounter(pfm::PerfEvent &&Event); + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_LIB_RISCV_RISCVCOUNTERS_H diff --git a/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp new file mode 100644 index 00000000000000..bf244c1992b4ef --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/RISCV/Target.cpp @@ -0,0 +1,409 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "../Target.h" + +#include "RISCVCounters.h" + +#include "MCTargetDesc/RISCVBaseInfo.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "MCTargetDesc/RISCVMatInt.h" +#include "RISCVInstrInfo.h" + +// include computeAvailableFeatures and computeRequiredFeatures. +#define GET_COMPUTE_FEATURES +#define GET_AVAILABLE_OPCODE_CHECKER +#include "RISCVGenInstrInfo.inc" +#undef GET_COMPUTE_FEATURES +#undef GET_AVAILABLE_OPCODE_CHECKER + +#include "llvm/CodeGen/MachineInstrBuilder.h" + +#include + +namespace llvm { +namespace exegesis { + +namespace { + +// TODO move perf counter data to td files (although it looks like an overkill +// of sorts) + +static const char *RISCVPfmCounterNames[] = { + "CPU_CYCLES", // 0 +}; + +static const PfmCountersInfo RISCVDefaultPfmCounters = { + RISCVPfmCounterNames[0], // Cycle counter + nullptr, // No uops counter. + nullptr, // No issue counters. + 0}; + +static const CpuAndPfmCounters RISCVCpuPfmCounters[] = { + {"", &RISCVDefaultPfmCounters}, +}; + +class ExegesisRISCVTarget : public ExegesisTarget { +public: + ExegesisRISCVTarget(); + + Expected> + createCounter(StringRef CounterName, const LLVMState &State, + ArrayRef ValidationCounters, + const pid_t ProcessID) const override; + + bool checkOpcodeSupported(int Opcode, + const MCSubtargetInfo &SI) const override; + + unsigned findRegisterByName(const StringRef RegName) const override; + + bool matchesArch(Triple::ArchType Arch) const override; + + std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, + const APInt &Value) const override; + + unsigned getDefaultLoopCounterRegister(const Triple &) const override; + + void decrementLoopCounterAndJump(MachineBasicBlock &MBB, + MachineBasicBlock &TargetMBB, + const MCInstrInfo &MII, + unsigned LoopRegister) const override; + + unsigned getScratchMemoryRegister(const Triple &TT) const override; + + void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg, + unsigned Offset) const override; + + virtual std::vector + storeRegValueToScratch(const MCSubtargetInfo &STI, unsigned Reg, + unsigned Offset) const override; + ArrayRef getUnavailableRegisters() const override; + + Error randomizeTargetMCOperand(const Instruction &Instr, const Variable &Var, + MCOperand &AssignedValue, + const BitVector &ForbiddenRegs) const override; + + void processInstructionReservedRegs(InstructionTemplate &IT) const override; + + std::vector + generateInstructionVariants(const Instruction &Instr, + unsigned MaxConfigsPerOpcode) const override; +}; + +ExegesisRISCVTarget::ExegesisRISCVTarget() + : ExegesisTarget(RISCVCpuPfmCounters, RISCV_MC::isOpcodeAvailable) {} + +Expected> ExegesisRISCVTarget::createCounter( + StringRef CounterName, const LLVMState &State, + ArrayRef ValidationCounters, const pid_t ProcessID) const { + if (CounterName == RISCVPfmCounterNames[0]) { + return createRISCVCpuCyclesCounter(pfm::PerfEvent(CounterName)); + } + return make_error(Twine("Unsupported performance counter '") + .concat(CounterName) + .concat("'")); +} + +bool ExegesisRISCVTarget::checkOpcodeSupported( + int Opcode, const MCSubtargetInfo &SI) const { + auto Features = SI.getFeatureBits(); + FeatureBitset AvailableFeatures = + RISCV_MC::computeAvailableFeatures(Features); + FeatureBitset RequiredFeatures = RISCV_MC::computeRequiredFeatures(Opcode); + FeatureBitset MissingFeatures = + (AvailableFeatures & RequiredFeatures) ^ RequiredFeatures; + return MissingFeatures.none(); +} + +#define GET_REGISTER_MATCHER +#include "RISCVGenAsmMatcher.inc" + +unsigned +ExegesisRISCVTarget::findRegisterByName(const StringRef RegName) const { + unsigned Reg; + if ((Reg = MatchRegisterName(RegName))) + return Reg; + if ((Reg = MatchRegisterAltName(RegName))) + return Reg; + return RISCV::NoRegister; +} + +bool ExegesisRISCVTarget::matchesArch(Triple::ArchType Arch) const { + return Arch == Triple::riscv32 || Arch == Triple::riscv64; +} + +// Stores constant value to a general-purpose (integer) register. +static std::vector loadIntReg(const MCSubtargetInfo &STI, unsigned Reg, + const APInt &Value) { + RISCVMatInt::InstSeq InstSeq = + RISCVMatInt::generateInstSeq(Value.getSExtValue(), STI); + // First instruction has form 'Op DestReg, X0, Imm' + MCRegister SrcReg = RISCV::X0; + MCRegister DestReg = Reg; + std::vector MatIntInstrs; + MatIntInstrs.reserve(InstSeq.size()); + for (const RISCVMatInt::Inst &Inst : InstSeq) { + if (Inst.getOpcode() == RISCV::LUI) { + MatIntInstrs.push_back( + MCInstBuilder(RISCV::LUI).addReg(DestReg).addImm(Inst.getImm())); + } else if (Inst.getOpcode() == RISCV::ADD_UW) { + MatIntInstrs.push_back(MCInstBuilder(RISCV::ADD_UW) + .addReg(DestReg) + .addReg(SrcReg) + .addReg(RISCV::X0)); + } else if (Inst.getOpcode() == RISCV::SH1ADD || + Inst.getOpcode() == RISCV::SH2ADD || + Inst.getOpcode() == RISCV::SH3ADD) { + MatIntInstrs.push_back(MCInstBuilder(Inst.getOpcode()) + .addReg(DestReg) + .addReg(SrcReg) + .addReg(SrcReg)); + } else { + MatIntInstrs.push_back(MCInstBuilder(Inst.getOpcode()) + .addReg(DestReg) + .addReg(SrcReg) + .addImm(Inst.getImm())); + } + // Further instructions have form 'Op DestReg, DestReg, Imm' + SrcReg = DestReg; + } + return MatIntInstrs; +} + +const unsigned ScratchIntReg = RISCV::X30; // t5 + +// Stores constant bits to a floating-point register. +static std::vector loadFPRegBits(const MCSubtargetInfo &STI, + unsigned Reg, const APInt &Bits, + unsigned FmvOpcode) { + std::vector Instrs = loadIntReg(STI, ScratchIntReg, Bits); + Instrs.push_back(MCInstBuilder(FmvOpcode).addReg(Reg).addReg(ScratchIntReg)); + return Instrs; +} + +// main idea is: +// we support APInt only if (represented as double) it have zero fractional +// part: 1.0, 2.0, 3.0, etc... then we can do the trick: write int to tmp reg t5 +// and then do FCVT this is only reliable thing in 32-bit mode, otherwise we +// need to use __floatsidf +static std::vector loadFP64RegBits32(const MCSubtargetInfo &STI, + unsigned Reg, const APInt &Bits) { + double D = Bits.bitsToDouble(); + double IPart; + double FPart = std::modf(D, &IPart); + + if (std::abs(FPart) > std::numeric_limits::epsilon()) { + errs() << "loadFP64RegBits32 is not implemented for doubles like " << D + << ", please remove fractional part\n"; + return {}; + } + + std::vector Instrs = loadIntReg(STI, ScratchIntReg, Bits); + Instrs.push_back( + MCInstBuilder(RISCV::FCVT_D_W).addReg(Reg).addReg(ScratchIntReg)); + return Instrs; +} + +static MCInst nop() { + // ADDI X0, X0, 0 + return MCInstBuilder(RISCV::ADDI) + .addReg(RISCV::X0) + .addReg(RISCV::X0) + .addImm(0); +} + +static bool isVectorRegList(unsigned Reg) { + return RISCV::VRM2RegClass.contains(Reg) || + RISCV::VRM4RegClass.contains(Reg) || + RISCV::VRM8RegClass.contains(Reg) || + RISCV::VRN2M1RegClass.contains(Reg) || + RISCV::VRN2M2RegClass.contains(Reg) || + RISCV::VRN2M4RegClass.contains(Reg) || + RISCV::VRN3M1RegClass.contains(Reg) || + RISCV::VRN3M2RegClass.contains(Reg) || + RISCV::VRN4M1RegClass.contains(Reg) || + RISCV::VRN4M2RegClass.contains(Reg) || + RISCV::VRN5M1RegClass.contains(Reg) || + RISCV::VRN6M1RegClass.contains(Reg) || + RISCV::VRN7M1RegClass.contains(Reg) || + RISCV::VRN8M1RegClass.contains(Reg); +} + +std::vector ExegesisRISCVTarget::setRegTo(const MCSubtargetInfo &STI, + unsigned Reg, + const APInt &Value) const { + if (RISCV::GPRRegClass.contains(Reg)) + return loadIntReg(STI, Reg, Value); + if (RISCV::FPR16RegClass.contains(Reg)) + return loadFPRegBits(STI, Reg, Value, RISCV::FMV_H_X); + if (RISCV::FPR32RegClass.contains(Reg)) + return loadFPRegBits(STI, Reg, Value, RISCV::FMV_W_X); + if (RISCV::FPR64RegClass.contains(Reg)) { + if (STI.hasFeature(RISCV::Feature64Bit)) + return loadFPRegBits(STI, Reg, Value, RISCV::FMV_D_X); + else + return loadFP64RegBits32(STI, Reg, Value); + } + if (Reg == RISCV::FRM || Reg == RISCV::VL || Reg == RISCV::VLENB || + Reg == RISCV::VTYPE || RISCV::GPRPairRegClass.contains(Reg) || + RISCV::VRRegClass.contains(Reg) || isVectorRegList(Reg)) { + // Don't initialize: + // - FRM + // - VL, VLENB, VTYPE + // - vector registers (and vector register lists) + // - Zfinx registers + // Generate 'NOP' so that exegesis treats such registers as initialized + // (it tries to initialize them with '0' anyway). + return {nop()}; + } + errs() << "setRegTo is not implemented for Reg " << Reg + << ", results will be unreliable\n"; + return {}; +} + +const unsigned DefaultLoopCounterReg = RISCV::X31; // t6 +const unsigned ScratchMemoryReg = RISCV::X10; // a0 + +unsigned +ExegesisRISCVTarget::getDefaultLoopCounterRegister(const Triple &) const { + return DefaultLoopCounterReg; +} + +void ExegesisRISCVTarget::decrementLoopCounterAndJump( + MachineBasicBlock &MBB, MachineBasicBlock &TargetMBB, + const MCInstrInfo &MII, unsigned LoopRegister) const { + BuildMI(&MBB, DebugLoc(), MII.get(RISCV::ADDI)) + .addDef(LoopRegister) + .addUse(LoopRegister) + .addImm(-1); + BuildMI(&MBB, DebugLoc(), MII.get(RISCV::BNE)) + .addUse(LoopRegister) + .addUse(RISCV::X0) + .addMBB(&TargetMBB); +} + +unsigned ExegesisRISCVTarget::getScratchMemoryRegister(const Triple &TT) const { + return ScratchMemoryReg; // a0 +} + +void ExegesisRISCVTarget::fillMemoryOperands(InstructionTemplate &IT, + unsigned Reg, + unsigned Offset) const { + // TODO: for now we ignore Offset because have no way + // to detect it in instruction. + auto &I = IT.getInstr(); + + auto MemOpIt = + find_if(I.Operands, [](Operand const &Op) { return Op.isMemory(); }); + assert(MemOpIt != I.Operands.end() && + "Instruction must have memory operands"); + + auto &MemOp = *MemOpIt; + + assert(MemOp.isReg() && "Memory operand expected to be register"); + + IT.getValueFor(MemOp) = MCOperand::createReg(Reg); +} + +std::vector ExegesisRISCVTarget::storeRegValueToScratch( + const MCSubtargetInfo &STI, unsigned Reg, unsigned Offset) const { + std::vector Ret; + + if (RISCV::GPRRegClass.contains(Reg)) { + Ret.push_back(MCInstBuilder(RISCV::SW) + .addReg(Reg) + .addReg(ScratchMemoryReg) + .addImm(Offset)); + return Ret; + } + + errs() << "Failed to store value of " << Reg << " register to scratch memory"; + return {nop()}; +} + +const unsigned UnavailableRegisters[4] = {RISCV::X0, DefaultLoopCounterReg, + ScratchIntReg, ScratchMemoryReg}; + +ArrayRef ExegesisRISCVTarget::getUnavailableRegisters() const { + return ArrayRef(UnavailableRegisters); +} + +Error ExegesisRISCVTarget::randomizeTargetMCOperand( + const Instruction &Instr, const Variable &Var, MCOperand &AssignedValue, + const BitVector &ForbiddenRegs) const { + uint8_t OperandType = + Instr.getPrimaryOperand(Var).getExplicitOperandInfo().OperandType; + + switch (OperandType) { + case RISCVOp::OPERAND_FRMARG: + AssignedValue = MCOperand::createImm(RISCVFPRndMode::DYN); + break; + case RISCVOp::OPERAND_SIMM10_LSB0000_NONZERO: + AssignedValue = MCOperand::createImm(0b1 << 4); + break; + case RISCVOp::OPERAND_SIMM6_NONZERO: + case RISCVOp::OPERAND_UIMMLOG2XLEN_NONZERO: + AssignedValue = MCOperand::createImm(1); + break; + default: + if (OperandType >= RISCVOp::OPERAND_FIRST_RISCV_IMM && + OperandType <= RISCVOp::OPERAND_LAST_RISCV_IMM) + AssignedValue = MCOperand::createImm(0); + } + return Error::success(); +} + +// Process instructions that used ReservedRegisters. +// We must not create instructions that used rd=x0. But for some of them in C +// extension we use registers in which we are really not going to write to. +// Registers were reserved in RISCVRegisterInfo.cpp using markSuperRegs function +// and should not be redeclared. Thus we must set appropriate register +// explicitly for each instruction according to RVC spec. +void ExegesisRISCVTarget::processInstructionReservedRegs( + InstructionTemplate &IT) const { + MCOperand &AssignedValue = IT.getValueFor(0); + + switch (IT.getOpcode()) { + case RISCV::C_ADDI16SP: + AssignedValue = MCOperand::createReg(RISCV::X2); + break; + case RISCV::C_ADDI_NOP: + case RISCV::C_ADD_HINT: + case RISCV::C_SLLI_HINT: + AssignedValue = MCOperand::createReg(RISCV::X0); + break; + default: + break; + } +} + +std::vector +ExegesisRISCVTarget::generateInstructionVariants( + const Instruction &Instr, unsigned int MaxConfigsPerOpcode) const { + InstructionTemplate IT{&Instr}; + for (const Operand &Op : Instr.Operands) { + if (Op.isMemory()) { + IT.getValueFor(Op) = MCOperand::createReg(ScratchMemoryReg); + } + } + return {IT}; +} + +} // anonymous namespace + +static ExegesisTarget *getTheRISCVExegesisTarget() { + static ExegesisRISCVTarget Target; + return &Target; +} + +void InitializeRISCVExegesisTarget() { + ExegesisTarget::registerTarget(getTheRISCVExegesisTarget()); +} + +} // namespace exegesis +} // namespace llvm diff --git a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp index 7100b51bbb7298..4971c5b6097254 100644 --- a/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp +++ b/llvm/tools/llvm-exegesis/lib/SerialSnippetGenerator.cpp @@ -52,13 +52,16 @@ computeAliasingInstructions(const LLVMState &State, const Instruction *Instr, continue; if (OtherOpcode == Instr->Description.getOpcode()) continue; + if (!State.getExegesisTarget().checkOpcodeSupported( + OtherOpcode, State.getSubtargetInfo())) + continue; const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode); const MCInstrDesc &OtherInstrDesc = OtherInstr.Description; // Ignore instructions that we cannot run. if (OtherInstrDesc.isPseudo() || OtherInstrDesc.usesCustomInsertionHook() || OtherInstrDesc.isBranch() || OtherInstrDesc.isIndirectBranch() || OtherInstrDesc.isCall() || OtherInstrDesc.isReturn()) { - continue; + continue; } if (OtherInstr.hasMemoryOperands()) continue; @@ -81,12 +84,10 @@ static ExecutionMode getExecutionModes(const Instruction &Instr, EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS; if (Instr.hasMemoryOperands()) EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR; - else { - if (Instr.hasAliasingRegisters(ForbiddenRegisters)) - EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS; - if (Instr.hasOneUseOrOneDef()) - EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR; - } + if (Instr.hasAliasingNotMemoryRegisters(ForbiddenRegisters)) + EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS; + if (Instr.hasOneUseOrOneDef()) + EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR; return EM; } @@ -104,6 +105,7 @@ static void appendCodeTemplates(const LLVMState &State, case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: { // Picking whatever value for the tied variable will make the instruction // serial. + State.getExegesisTarget().processInstructionReservedRegs(Variant); CodeTemplate CT; CT.Execution = ExecutionModeBit; CT.Info = std::string(ExecutionClassDescription); @@ -113,7 +115,51 @@ static void appendCodeTemplates(const LLVMState &State, } case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: { // Select back-to-back memory instruction. - // TODO: Implement me. + + auto &I = Variant.getInstr(); + if (I.Description.mayLoad()) { + // If instruction is load, we can self-alias it in case when instruction + // overrides whole address register. For that we use provided scratch + // memory. + + // TODO: now it is not checked if load writes the whole register. + + auto DefOpIt = find_if(I.Operands, [](Operand const &op) { + return op.isDef() && op.isReg(); + }); + + if (DefOpIt == I.Operands.end()) + return; + + auto &DefOp = *DefOpIt; + auto &ET = State.getExegesisTarget(); + auto ScratchMemoryRegister = ET.getScratchMemoryRegister( + State.getTargetMachine().getTargetTriple()); + auto &RegClass = + State.getTargetMachine().getMCRegisterInfo()->getRegClass( + DefOp.getExplicitOperandInfo().RegClass); + + // Register classes of def operand and memory operand must be the same + // to perform aliasing. + if (!RegClass.contains(ScratchMemoryRegister)) + return; + + ET.fillMemoryOperands(Variant, ScratchMemoryRegister, 0); + Variant.getValueFor(DefOp) = MCOperand::createReg(ScratchMemoryRegister); + + CodeTemplate CT; + CT.Execution = ExecutionModeBit; + if (CT.ScratchSpacePointerInReg == 0) + CT.ScratchSpacePointerInReg = ScratchMemoryRegister; + + CT.Info = std::string(ExecutionClassDescription); + CT.Instructions.push_back(std::move(Variant)); + CT.PreinitScratchMemory.emplace_back(ScratchMemoryRegister, + /* Offset */ 0); + CodeTemplates.push_back(std::move(CT)); + } + + // TODO: implement more cases return; } case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: { diff --git a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp b/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp index 431d99c72b8086..27550860765547 100644 --- a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp +++ b/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp @@ -36,10 +36,11 @@ namespace { class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer { public: explicit BenchmarkCodeStreamer( - MCContext *Context, const DenseMap &RegNameToRegNo, + const ExegesisTarget &Target, MCContext *Context, + const DenseMap &RegNameToRegNo, BenchmarkCode *Result) - : MCStreamer(*Context), RegNameToRegNo(RegNameToRegNo), Result(Result) {} - + : MCStreamer(*Context), Target(Target), RegNameToRegNo(RegNameToRegNo), + Result(Result) {} // Implementation of the MCStreamer interface. We only care about // instructions. void emitInstruction(const MCInst &Instruction, @@ -207,14 +208,15 @@ class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer { Align ByteAlignment, SMLoc Loc) override {} unsigned findRegisterByName(const StringRef RegName) const { + if (unsigned Reg = Target.findRegisterByName(RegName)) + return Reg; auto Iter = RegNameToRegNo.find(RegName); if (Iter != RegNameToRegNo.end()) return Iter->second; - errs() << "'" << RegName - << "' is not a valid register name for the target\n"; - return 0; + report_fatal_error("'" + RegName + + "' is not a valid register name for the target\n"); } - + const ExegesisTarget &Target; const DenseMap &RegNameToRegNo; BenchmarkCode *const Result; unsigned InvalidComments = 0; @@ -248,8 +250,8 @@ Expected> readSnippets(const LLVMState &State, TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false)); Context.setObjectFileInfo(ObjectFileInfo.get()); Context.initInlineSourceManager(); - BenchmarkCodeStreamer Streamer(&Context, State.getRegNameToRegNoMapping(), - &Result); + BenchmarkCodeStreamer Streamer(State.getExegesisTarget(), &Context, + State.getRegNameToRegNoMapping(), &Result); std::string Error; raw_string_ostream ErrorStream(Error); diff --git a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp index 7dcff60a8fd11f..8eaf83044e0dd1 100644 --- a/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp +++ b/llvm/tools/llvm-exegesis/lib/SnippetGenerator.cpp @@ -73,6 +73,9 @@ Error SnippetGenerator::generateConfigurations( for (CodeTemplate &CT : Templates) { // TODO: Generate as many BenchmarkCode as needed. { + CT.ScratchSpacePointerInReg = + State.getExegesisTarget().getScratchMemoryRegister( + State.getTargetMachine().getTargetTriple()); BenchmarkCode BC; BC.Info = CT.Info; BC.Key.Instructions.reserve(CT.Instructions.size()); @@ -108,6 +111,12 @@ std::vector SnippetGenerator::computeRegisterInitialValues( // Loop invariant: DefinedRegs[i] is true iif it has been set at least once // before the current instruction. BitVector DefinedRegs = State.getRATC().emptyRegisters(); + // If target always expects a scratch memory register as live input, + // mark it as defined. + const ExegesisTarget &Target = State.getExegesisTarget(); + unsigned ScratchMemoryReg = Target.getScratchMemoryRegister( + State.getTargetMachine().getTargetTriple()); + DefinedRegs.set(ScratchMemoryReg); std::vector RIV; for (const InstructionTemplate &IT : Instructions) { // Returns the register that this Operand sets or uses, or 0 if this is not @@ -200,7 +209,9 @@ static void setRegisterOperandValue(const RegisterOperandAssignment &ROV, if (ROV.Op->isExplicit()) { auto &AssignedValue = IB.getValueFor(*ROV.Op); if (AssignedValue.isValid()) { - assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg); + // TODO don't re-assign register operands which are already "locked" + // by Target in corresponding InstructionTemplate + // assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg); return; } AssignedValue = MCOperand::createReg(ROV.Reg); diff --git a/llvm/tools/llvm-exegesis/lib/Target.cpp b/llvm/tools/llvm-exegesis/lib/Target.cpp index 29e58692f0e92b..533f709147eb62 100644 --- a/llvm/tools/llvm-exegesis/lib/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/Target.cpp @@ -212,6 +212,10 @@ class ExegesisDefaultTarget : public ExegesisTarget { ExegesisDefaultTarget() : ExegesisTarget({}, opcodeIsNotAvailable) {} private: + unsigned findRegisterByName(const StringRef RegName) const override { + llvm_unreachable("Not yet implemented"); + } + std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const override { llvm_unreachable("Not yet implemented"); diff --git a/llvm/tools/llvm-exegesis/lib/Target.h b/llvm/tools/llvm-exegesis/lib/Target.h index 522c75d15703d5..96088f88a89a94 100644 --- a/llvm/tools/llvm-exegesis/lib/Target.h +++ b/llvm/tools/llvm-exegesis/lib/Target.h @@ -86,6 +86,14 @@ class ExegesisTarget { ArrayRef ValidationCounters, const pid_t ProcessID = 0) const; + virtual bool checkOpcodeSupported(int Opcode, + const MCSubtargetInfo &SI) const { + return true; + } + + // Find register by name, NoRegister if not found. + virtual unsigned findRegisterByName(const StringRef RegName) const = 0; + // Targets can use this to add target-specific passes in assembleToStream(); virtual void addTargetSpecificPasses(PassManagerBase &PM) const {} @@ -201,6 +209,14 @@ class ExegesisTarget { "fillMemoryOperands() requires getScratchMemoryRegister() > 0"); } + // Generates code to store register into scratch memory with offset. + virtual std::vector storeRegValueToScratch(const MCSubtargetInfo &STI, + unsigned Reg, + unsigned Offset) const { + llvm_unreachable( + "storeRegValueToScratch() requires getScratchMemoryRegister() > 0"); + } + // Returns a counter usable as a loop counter. virtual unsigned getDefaultLoopCounterRegister(const Triple &) const { return 0; @@ -238,6 +254,9 @@ class ExegesisTarget { "targets with target-specific operands should implement this"); } + // Process instructions that used reserved registers. + virtual void processInstructionReservedRegs(InstructionTemplate &IT) const {} + // Returns true if this instruction is supported as a back-to-back // instructions. // FIXME: Eventually we should discover this dynamically. diff --git a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp index dbb2bf409316e7..26bdd3f7e0c9be 100644 --- a/llvm/tools/llvm-exegesis/lib/X86/Target.cpp +++ b/llvm/tools/llvm-exegesis/lib/X86/Target.cpp @@ -736,6 +736,8 @@ class ExegesisX86Target : public ExegesisTarget { const MCInstrInfo &MII, unsigned LoopRegister) const override; + unsigned findRegisterByName(const StringRef RegName) const override; + std::vector setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const override; @@ -1009,6 +1011,10 @@ static std::vector loadImmediateSegmentRegister(unsigned Reg, #endif // defined(__x86_64__) && defined(__linux__) } +unsigned findRegisterByName(const StringRef RegName) const { + return X86::NoRegister; +} + std::vector ExegesisX86Target::setRegTo(const MCSubtargetInfo &STI, unsigned Reg, const APInt &Value) const { diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp index 06e1c7f3c1bbed..d286de90054244 100644 --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -57,7 +57,9 @@ static cl::opt OpcodeIndex( static cl::opt OpcodeNames("opcode-name", - cl::desc("comma-separated list of opcodes to measure, by name"), + cl::desc("comma-separated list of opcodes to measure, " + "each item is either opcode name ('OP') " + "or opcode range ('OP1..OP2', ends are inclusive)"), cl::cat(BenchmarkOptions), cl::init("")); static cl::opt SnippetsFile("snippets-file", @@ -235,7 +237,7 @@ static cl::opt static cl::opt MCPU("mcpu", cl::desc("Target a specific cpu type (-mcpu=help for details)"), - cl::value_desc("cpu-name"), cl::cat(Options), cl::init("native")); + cl::value_desc("cpu-name"), cl::cat(Options), cl::init("")); static cl::opt DumpObjectToDisk("dump-object-to-disk", @@ -269,6 +271,10 @@ static cl::list ValidationCounters( "counter to validate benchmarking assumptions"), cl::CommaSeparated, cl::cat(BenchmarkOptions), ValidationEventOptions()); +static cl::opt MAttr( + "mattr", cl::desc("comma-separated list of target architecture features"), + cl::value_desc("+feature1,-feature2,..."), cl::cat(Options), cl::init("")); + static ExitOnError ExitOnErr("llvm-exegesis error: "); // Helper function that logs the error(s) and exits. @@ -291,6 +297,22 @@ T ExitOnFileError(const Twine &FileName, Expected &&E) { return std::move(*E); } +static const char *getIgnoredOpcodeReasonOrNull(const LLVMState &State, + unsigned Opcode) { + const MCInstrDesc &InstrDesc = State.getIC().getInstr(Opcode).Description; + if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook()) + return "Unsupported opcode: isPseudo/usesCustomInserter"; + if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch()) + return "Unsupported opcode: isBranch/isIndirectBranch"; + if (InstrDesc.isCall() || InstrDesc.isReturn()) + return "Unsupported opcode: isCall/isReturn"; + return nullptr; +} + +static bool isIgnoredOpcode(const LLVMState &State, unsigned Opcode) { + return getIgnoredOpcodeReasonOrNull(State, Opcode) != nullptr; +} + // Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided, // and returns the opcode indices or {} if snippets should be read from // `SnippetsFile`. @@ -335,10 +357,32 @@ static std::vector getOpcodesOrDie(const LLVMState &State) { std::vector Result; Result.reserve(Pieces.size()); for (const StringRef &OpcodeName : Pieces) { - if (unsigned Opcode = ResolveName(OpcodeName)) + if (unsigned Opcode = ResolveName(OpcodeName)) { Result.push_back(Opcode); - else + continue; + } + // Not a known opcode name; should be an opcode name range. + size_t DotDotPos = OpcodeName.find(".."); + if (DotDotPos == StringRef::npos) { ExitWithError(Twine("unknown opcode ").concat(OpcodeName)); + } + StringRef BeginOpcodeName = OpcodeName.substr(0, DotDotPos); + unsigned BeginOpcode = + BeginOpcodeName.empty() ? 1 : ResolveName(BeginOpcodeName); + if (BeginOpcode == 0) { + ExitWithError(Twine("unknown opcode ").concat(BeginOpcodeName)); + } + StringRef EndOpcodeName = OpcodeName.substr(DotDotPos + 2); + unsigned EndOpcode = EndOpcodeName.empty() + ? State.getInstrInfo().getNumOpcodes() - 1 + : ResolveName(EndOpcodeName); + if (EndOpcode == 0) { + ExitWithError(Twine("unknown opcode ").concat(EndOpcodeName)); + } + for (unsigned I = BeginOpcode; I <= EndOpcode; ++I) { + if (!isIgnoredOpcode(State, I)) + Result.push_back(I); + } } return Result; } @@ -347,17 +391,11 @@ static std::vector getOpcodesOrDie(const LLVMState &State) { static Expected> generateSnippets(const LLVMState &State, unsigned Opcode, const BitVector &ForbiddenRegs) { - const Instruction &Instr = State.getIC().getInstr(Opcode); - const MCInstrDesc &InstrDesc = Instr.Description; // Ignore instructions that we cannot run. - if (InstrDesc.isPseudo() || InstrDesc.usesCustomInsertionHook()) - return make_error( - "Unsupported opcode: isPseudo/usesCustomInserter"); - if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch()) - return make_error("Unsupported opcode: isBranch/isIndirectBranch"); - if (InstrDesc.isCall() || InstrDesc.isReturn()) - return make_error("Unsupported opcode: isCall/isReturn"); + if (const char *Reason = getIgnoredOpcodeReasonOrNull(State, Opcode)) + return make_error(Reason); + const Instruction &Instr = State.getIC().getInstr(Opcode); const std::vector InstructionVariants = State.getExegesisTarget().generateInstructionVariants( Instr, MaxConfigsPerOpcode); @@ -474,8 +512,8 @@ void benchmarkMain() { InitializeAllAsmParsers(); InitializeAllExegesisTargets(); - const LLVMState State = - ExitOnErr(LLVMState::Create(TripleName, MCPU, "", UseDummyPerfCounters)); + const LLVMState State = ExitOnErr( + LLVMState::Create(TripleName, MCPU, MAttr, UseDummyPerfCounters)); // Preliminary check to ensure features needed for requested // benchmark mode are present on target CPU and/or OS.