[llvm] [RISCV][GlobalISel] Legalize readcyclecounter/readsteadycounter (PR #217535)
Kane Wang via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 19:36:47 PDT 2026
https://github.com/ReVe1uv updated https://github.com/llvm/llvm-project/pull/217535
>From 7a017b0161d0c2876e1467eda006c19eeb5947ce Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Thu, 20 Aug 2026 14:18:37 +0800
Subject: [PATCH 1/5] [RISCV][GlobalISel] Legalize
readcyclecounter/readsteadycounter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add a legalizer rule for G_READCYCLECOUNTER/G_READSTEADYCOUNTER. On RV64
they are legal and select via the existing tablegen Pats (rdcycle/rdtime).
On RV32 lower them to the ReadCounterWide target pseudo, which FinalizeISel
expands into the re-read-the-high-half loop (emitReadCounterWidePseudo) —
the same expansion SelectionDAG uses.
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 41 +++++++++++++++++++
.../Target/RISCV/GISel/RISCVLegalizerInfo.h | 2 +
.../GlobalISel/legalizer-info-validation.mir | 9 ++--
.../RISCV/GlobalISel/readcyclecounter.ll | 24 +++++++++++
.../RISCV/GlobalISel/readsteadycounter.ll | 24 +++++++++++
5 files changed, 96 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/readcyclecounter.ll
create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/readsteadycounter.ll
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 22f60dc0c1c24..57aead67c770e 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -542,6 +542,12 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
getActionDefinitionsBuilder({G_DYN_STACKALLOC, G_STACKSAVE, G_STACKRESTORE})
.lower();
+ // On RV64 the 64-bit counter CSRs (cycle/time) are read directly. On RV32
+ // they are custom-legally lowered to the ReadCounterWide target pseudo.
+ getActionDefinitionsBuilder({G_READCYCLECOUNTER, G_READSTEADYCOUNTER})
+ .legalFor(ST.is64Bit(), {s64})
+ .customFor(!ST.is64Bit(), {s64});
+
// FP Operations
// FIXME: Support s128 for rv32 when libcall handling is able to use sret.
@@ -889,6 +895,38 @@ bool RISCVLegalizerInfo::legalizeVAStart(MachineInstr &MI,
return true;
}
+bool RISCVLegalizerInfo::legalizeReadCounter(
+ MachineInstr &MI, MachineIRBuilder &MIRBuilder) const {
+ assert((MI.getOpcode() == TargetOpcode::G_READCYCLECOUNTER ||
+ MI.getOpcode() == TargetOpcode::G_READSTEADYCOUNTER) &&
+ "Unexpected opcode");
+ assert(!STI.is64Bit() && "READCYCLECOUNTER/READSTEADYCOUNTER only "
+ "has custom type legalization on riscv32");
+
+ // On RV32 a 64-bit counter CSR must be read as two 32-bit halves. Lower to
+ // the ReadCounterWide target pseudo.
+ bool IsCycle = MI.getOpcode() == TargetOpcode::G_READCYCLECOUNTER;
+ int64_t LoCounter = IsCycle ? RISCVSysReg::cycle : RISCVSysReg::time;
+ int64_t HiCounter = IsCycle ? RISCVSysReg::cycleh : RISCVSysReg::timeh;
+
+ MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+ auto CreateGPR = [&]() {
+ Register R = MRI.createGenericVirtualRegister(LLT::scalar(32));
+ MRI.setRegClass(R, &RISCV::GPRRegClass);
+ return R;
+ };
+ Register LoReg = CreateGPR();
+ Register HiReg = CreateGPR();
+
+ Register DstReg = MI.getOperand(0).getReg();
+ MIRBuilder.setDebugLoc(MI.getDebugLoc());
+ MIRBuilder.buildInstr(RISCV::ReadCounterWide, {LoReg, HiReg},
+ {LoCounter, HiCounter});
+ MIRBuilder.buildMergeValues(DstReg, {LoReg, HiReg});
+ MI.eraseFromParent();
+ return true;
+}
+
bool RISCVLegalizerInfo::legalizeBRJT(MachineInstr &MI,
MachineIRBuilder &MIRBuilder) const {
MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
@@ -1601,6 +1639,9 @@ bool RISCVLegalizerInfo::legalizeCustom(
Helper.Observer.changedInstr(MI);
return true;
}
+ case TargetOpcode::G_READCYCLECOUNTER:
+ case TargetOpcode::G_READSTEADYCOUNTER:
+ return legalizeReadCounter(MI, MIRBuilder);
case TargetOpcode::G_IS_FPCLASS: {
Register GISFPCLASS = MI.getOperand(0).getReg();
Register Src = MI.getOperand(1).getReg();
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
index 4b2f794f3843a..13ff6f7b57a34 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
@@ -45,6 +45,8 @@ class RISCVLegalizerInfo : public LegalizerInfo {
bool legalizeBitreverse(MachineInstr &MI, MachineIRBuilder &MIB) const;
bool legalizeBRJT(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
bool legalizeVAStart(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
+ bool legalizeReadCounter(MachineInstr &MI,
+ MachineIRBuilder &MIRBuilder) const;
bool legalizeVScale(MachineInstr &MI, MachineIRBuilder &MIB) const;
bool legalizeExt(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
bool legalizeSplatVector(MachineInstr &MI, MachineIRBuilder &MIB) const;
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index 1770bc6430eb9..653a6ee679cd2 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -197,11 +197,12 @@
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
# DEBUG-NEXT: G_READCYCLECOUNTER (opcode {{[0-9]+}}): 1 type index, 0 imm indices
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. the first uncovered type index: 1, OK
+# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
# DEBUG-NEXT: G_READSTEADYCOUNTER (opcode {{[0-9]+}}): 1 type index, 0 imm indices
-# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
+# DEBUG-NEXT: .. the first uncovered type index: 1, OK
+# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
# DEBUG-NEXT: G_LOAD (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/readcyclecounter.ll b/llvm/test/CodeGen/RISCV/GlobalISel/readcyclecounter.ll
new file mode 100644
index 0000000000000..ff24b0da57b00
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/readcyclecounter.ll
@@ -0,0 +1,24 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel < %s | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -global-isel < %s | FileCheck -check-prefix=RV64I %s
+
+; Verify that we lower @llvm.readcyclecounter() correctly.
+
+define i64 @test_builtin_readcyclecounter() nounwind {
+; RV32I-LABEL: test_builtin_readcyclecounter:
+; RV32I: # %bb.0:
+; RV32I-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1
+; RV32I-NEXT: rdcycleh a1
+; RV32I-NEXT: rdcycle a0
+; RV32I-NEXT: rdcycleh a2
+; RV32I-NEXT: bne a1, a2, .LBB0_1
+; RV32I-NEXT: # %bb.2:
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: test_builtin_readcyclecounter:
+; RV64I: # %bb.0:
+; RV64I-NEXT: rdcycle a0
+; RV64I-NEXT: ret
+ %1 = tail call i64 @llvm.readcyclecounter()
+ ret i64 %1
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/readsteadycounter.ll b/llvm/test/CodeGen/RISCV/GlobalISel/readsteadycounter.ll
new file mode 100644
index 0000000000000..49b99d8f302f4
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/readsteadycounter.ll
@@ -0,0 +1,24 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -global-isel < %s | FileCheck -check-prefix=RV32I %s
+; RUN: llc -mtriple=riscv64 -global-isel < %s | FileCheck -check-prefix=RV64I %s
+
+; Verify that we lower @llvm.readsteadycounter() correctly.
+
+define i64 @test_builtin_readsteadycounter() nounwind {
+; RV32I-LABEL: test_builtin_readsteadycounter:
+; RV32I: # %bb.0:
+; RV32I-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1
+; RV32I-NEXT: rdtimeh a1
+; RV32I-NEXT: rdtime a0
+; RV32I-NEXT: rdtimeh a2
+; RV32I-NEXT: bne a1, a2, .LBB0_1
+; RV32I-NEXT: # %bb.2:
+; RV32I-NEXT: ret
+;
+; RV64I-LABEL: test_builtin_readsteadycounter:
+; RV64I: # %bb.0:
+; RV64I-NEXT: rdtime a0
+; RV64I-NEXT: ret
+ %1 = tail call i64 @llvm.readsteadycounter()
+ ret i64 %1
+}
>From 8cdd89f1b726e41c0dd2a7d32a58edb4f656ff9d Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Thu, 20 Aug 2026 17:13:24 +0800
Subject: [PATCH 2/5] [RISCV][GlobalISel] Emit readcyclecounter loop directly
instead of via pseudo
GlobalISel can introduce control flow directly in the legalizer, so emit
the re-read-the-high-half csrrs/bne loop there rather than lowering to the
ReadCounterWide target pseudo and deferring to FinalizeISel. That pseudo
only exists to work around SelectionDAG's inability to introduce control
flow during lowering; GISel does not share that limitation.
Assisted-by: Claude
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 98 ++++++++++++++++---
.../Target/RISCV/GISel/RISCVLegalizerInfo.h | 4 +-
2 files changed, 86 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 57aead67c770e..05fb374a3bb15 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -543,7 +543,8 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.lower();
// On RV64 the 64-bit counter CSRs (cycle/time) are read directly. On RV32
- // they are custom-legally lowered to the ReadCounterWide target pseudo.
+ // they are custom-legally lowered to a re-read-the-high-half loop (see
+ // legalizeReadCounter).
getActionDefinitionsBuilder({G_READCYCLECOUNTER, G_READSTEADYCOUNTER})
.legalFor(ST.is64Bit(), {s64})
.customFor(!ST.is64Bit(), {s64});
@@ -896,34 +897,103 @@ bool RISCVLegalizerInfo::legalizeVAStart(MachineInstr &MI,
}
bool RISCVLegalizerInfo::legalizeReadCounter(
- MachineInstr &MI, MachineIRBuilder &MIRBuilder) const {
+ MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+ GISelChangeObserver &Observer) const {
assert((MI.getOpcode() == TargetOpcode::G_READCYCLECOUNTER ||
MI.getOpcode() == TargetOpcode::G_READSTEADYCOUNTER) &&
"Unexpected opcode");
assert(!STI.is64Bit() && "READCYCLECOUNTER/READSTEADYCOUNTER only "
"has custom type legalization on riscv32");
- // On RV32 a 64-bit counter CSR must be read as two 32-bit halves. Lower to
- // the ReadCounterWide target pseudo.
- bool IsCycle = MI.getOpcode() == TargetOpcode::G_READCYCLECOUNTER;
- int64_t LoCounter = IsCycle ? RISCVSysReg::cycle : RISCVSysReg::time;
- int64_t HiCounter = IsCycle ? RISCVSysReg::cycleh : RISCVSysReg::timeh;
+ // On RV32 a 64-bit counter CSR must be read as two 32-bit halves. Because
+ // the count may wrap between the two reads, re-read the high half and loop
+ // until the two high reads agree.
+ int64_t LoCounter, HiCounter;
+ if (MI.getOpcode() == TargetOpcode::G_READCYCLECOUNTER) {
+ LoCounter = RISCVSysReg::cycle;
+ HiCounter = RISCVSysReg::cycleh;
+ } else {
+ LoCounter = RISCVSysReg::time;
+ HiCounter = RISCVSysReg::timeh;
+ }
+ MachineBasicBlock *BB = MI.getParent();
+ MachineFunction &MF = *BB->getParent();
+ const BasicBlock *LLVMBB = BB->getBasicBlock();
+ DebugLoc DL = MI.getDebugLoc();
MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
+
+ // Split BB into an entry that falls through into a loop block, and a done
+ // block that receives the remainder of BB and its original successors.
+ MachineFunction::iterator It = std::next(BB->getIterator());
+ MachineBasicBlock *LoopMBB = MF.CreateMachineBasicBlock(LLVMBB);
+ MachineBasicBlock *DoneMBB = MF.CreateMachineBasicBlock(LLVMBB);
+ MF.insert(It, LoopMBB);
+ MF.insert(It, DoneMBB);
+
+ // Splice the instructions after the readcyclecounter into DoneMBB, notifying
+ // the observer about each moved instruction so CSEInfo stays consistent.
+ SmallVector<MachineInstr *, 4> MovedInstrs;
+ for (MachineBasicBlock::iterator I = std::next(MI.getIterator()),
+ E = BB->end();
+ I != E; ++I)
+ MovedInstrs.push_back(&*I);
+ for (MachineInstr *MovedMI : MovedInstrs)
+ Observer.changingInstr(*MovedMI);
+ DoneMBB->splice(DoneMBB->begin(), BB,
+ std::next(MachineBasicBlock::iterator(MI)), BB->end());
+ for (MachineInstr *MovedMI : MovedInstrs)
+ Observer.changedInstr(*MovedMI);
+ DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
+ BB->addSuccessor(LoopMBB);
+
+ LLT S32 = LLT::scalar(32);
+ // Generic vregs carry the s32 type for G_MERGE_VALUES below, but are also
+ // constrained to GPR so the target CSRRS/BNE instructions satisfy the
+ // verifier's register-class constraints.
auto CreateGPR = [&]() {
- Register R = MRI.createGenericVirtualRegister(LLT::scalar(32));
+ Register R = MRI.createGenericVirtualRegister(S32);
MRI.setRegClass(R, &RISCV::GPRRegClass);
return R;
};
- Register LoReg = CreateGPR();
Register HiReg = CreateGPR();
+ Register LoReg = CreateGPR();
+ Register ReadAgainReg = CreateGPR();
+
+ // read:
+ // csrrs HiReg, counterh # high word
+ // csrrs LoReg, counter # low word
+ // csrrs ReadAgainReg, counterh
+ // bne HiReg, ReadAgainReg, read
+ // Build the target instructions fully before inserting so the change
+ // observer (CSEInfo) and the legalizer worklist see their final form;
+ // raw BuildMI or chaining after insertInstr would bypass the observer.
+ MIRBuilder.setInsertPt(*LoopMBB, LoopMBB->begin());
+ MIRBuilder.setDebugLoc(DL);
+ auto BuildCSRRS = [&](Register Dst, int64_t Csr) {
+ MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert(RISCV::CSRRS);
+ MIB.addReg(Dst, RegState::Define).addImm(Csr).addReg(RISCV::X0);
+ MIRBuilder.insertInstr(MIB);
+ };
+ BuildCSRRS(HiReg, HiCounter);
+ BuildCSRRS(LoReg, LoCounter);
+ BuildCSRRS(ReadAgainReg, HiCounter);
+ MachineInstrBuilder BNE = MIRBuilder.buildInstrNoInsert(RISCV::BNE);
+ BNE.addReg(HiReg).addReg(ReadAgainReg).addMBB(LoopMBB);
+ MIRBuilder.insertInstr(BNE);
+
+ LoopMBB->addSuccessor(LoopMBB);
+ LoopMBB->addSuccessor(DoneMBB);
+
+ // Re-pair the two halves into the 64-bit result.
Register DstReg = MI.getOperand(0).getReg();
- MIRBuilder.setDebugLoc(MI.getDebugLoc());
- MIRBuilder.buildInstr(RISCV::ReadCounterWide, {LoReg, HiReg},
- {LoCounter, HiCounter});
- MIRBuilder.buildMergeValues(DstReg, {LoReg, HiReg});
+ Observer.erasingInstr(MI);
MI.eraseFromParent();
+
+ MIRBuilder.setInsertPt(*DoneMBB, DoneMBB->begin());
+ MIRBuilder.setDebugLoc(DL);
+ MIRBuilder.buildMergeValues(DstReg, {LoReg, HiReg});
return true;
}
@@ -1641,7 +1711,7 @@ bool RISCVLegalizerInfo::legalizeCustom(
}
case TargetOpcode::G_READCYCLECOUNTER:
case TargetOpcode::G_READSTEADYCOUNTER:
- return legalizeReadCounter(MI, MIRBuilder);
+ return legalizeReadCounter(MI, MIRBuilder, Helper.Observer);
case TargetOpcode::G_IS_FPCLASS: {
Register GISFPCLASS = MI.getOperand(0).getReg();
Register Src = MI.getOperand(1).getReg();
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
index 13ff6f7b57a34..639e68401e2da 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.h
@@ -45,8 +45,8 @@ class RISCVLegalizerInfo : public LegalizerInfo {
bool legalizeBitreverse(MachineInstr &MI, MachineIRBuilder &MIB) const;
bool legalizeBRJT(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
bool legalizeVAStart(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
- bool legalizeReadCounter(MachineInstr &MI,
- MachineIRBuilder &MIRBuilder) const;
+ bool legalizeReadCounter(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+ GISelChangeObserver &Observer) const;
bool legalizeVScale(MachineInstr &MI, MachineIRBuilder &MIB) const;
bool legalizeExt(MachineInstr &MI, MachineIRBuilder &MIRBuilder) const;
bool legalizeSplatVector(MachineInstr &MI, MachineIRBuilder &MIB) const;
>From c9585dbbbdb75301ea5546002acfb598cbb1c699 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Thu, 20 Aug 2026 18:31:06 +0800
Subject: [PATCH 3/5] [RISCV][GlobalISel] Use BuildMI directly for
readcyclecounter loop
The legalizer emits real target instructions (CSRRS/BNE), so use BuildMI
directly rather than routing them through MIRBuilder.buildInstrNoInsert.
---
.../Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 35 +++++++++----------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 05fb374a3bb15..e61612e45b67f 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -956,8 +956,8 @@ bool RISCVLegalizerInfo::legalizeReadCounter(
MRI.setRegClass(R, &RISCV::GPRRegClass);
return R;
};
- Register HiReg = CreateGPR();
Register LoReg = CreateGPR();
+ Register HiReg = CreateGPR();
Register ReadAgainReg = CreateGPR();
// read:
@@ -965,23 +965,22 @@ bool RISCVLegalizerInfo::legalizeReadCounter(
// csrrs LoReg, counter # low word
// csrrs ReadAgainReg, counterh
// bne HiReg, ReadAgainReg, read
- // Build the target instructions fully before inserting so the change
- // observer (CSEInfo) and the legalizer worklist see their final form;
- // raw BuildMI or chaining after insertInstr would bypass the observer.
- MIRBuilder.setInsertPt(*LoopMBB, LoopMBB->begin());
- MIRBuilder.setDebugLoc(DL);
- auto BuildCSRRS = [&](Register Dst, int64_t Csr) {
- MachineInstrBuilder MIB = MIRBuilder.buildInstrNoInsert(RISCV::CSRRS);
- MIB.addReg(Dst, RegState::Define).addImm(Csr).addReg(RISCV::X0);
- MIRBuilder.insertInstr(MIB);
- };
- BuildCSRRS(HiReg, HiCounter);
- BuildCSRRS(LoReg, LoCounter);
- BuildCSRRS(ReadAgainReg, HiCounter);
-
- MachineInstrBuilder BNE = MIRBuilder.buildInstrNoInsert(RISCV::BNE);
- BNE.addReg(HiReg).addReg(ReadAgainReg).addMBB(LoopMBB);
- MIRBuilder.insertInstr(BNE);
+ // Emit the target instructions directly with BuildMI.
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), HiReg)
+ .addImm(HiCounter)
+ .addReg(RISCV::X0);
+ BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), LoReg)
+ .addImm(LoCounter)
+ .addReg(RISCV::X0);
+ BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), ReadAgainReg)
+ .addImm(HiCounter)
+ .addReg(RISCV::X0);
+
+ BuildMI(LoopMBB, DL, TII->get(RISCV::BNE))
+ .addReg(HiReg)
+ .addReg(ReadAgainReg)
+ .addMBB(LoopMBB);
LoopMBB->addSuccessor(LoopMBB);
LoopMBB->addSuccessor(DoneMBB);
>From 034220c766670025c6c1bbf0b2f489789c28feec Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Fri, 21 Aug 2026 10:26:19 +0800
Subject: [PATCH 4/5] [RISCV][GlobalISel] Use RISCVInstrInfo for
readcyclecounter loop
Use STI.getInstrInfo() (RISCVInstrInfo*) instead of the generic
TargetInstrInfo from MF.getSubtarget().
---
llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index e61612e45b67f..c3058023851c6 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -966,7 +966,7 @@ bool RISCVLegalizerInfo::legalizeReadCounter(
// csrrs ReadAgainReg, counterh
// bne HiReg, ReadAgainReg, read
// Emit the target instructions directly with BuildMI.
- const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), HiReg)
.addImm(HiCounter)
.addReg(RISCV::X0);
>From 8928f3e7d1fa77d6ba98ec210baafe23c4e44d4c Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Mon, 24 Aug 2026 10:36:03 +0800
Subject: [PATCH 5/5] [RISCV][GlobalISel][NFC] Drop MovedInstrs vector in
readcyclecounter loop
Iterate BB before the splice and DoneMBB after it directly, instead of
collecting moved instructions into a SmallVector first.
---
llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index c3058023851c6..1175468e7f2ca 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -933,17 +933,14 @@ bool RISCVLegalizerInfo::legalizeReadCounter(
// Splice the instructions after the readcyclecounter into DoneMBB, notifying
// the observer about each moved instruction so CSEInfo stays consistent.
- SmallVector<MachineInstr *, 4> MovedInstrs;
for (MachineBasicBlock::iterator I = std::next(MI.getIterator()),
E = BB->end();
I != E; ++I)
- MovedInstrs.push_back(&*I);
- for (MachineInstr *MovedMI : MovedInstrs)
- Observer.changingInstr(*MovedMI);
+ Observer.changingInstr(*I);
DoneMBB->splice(DoneMBB->begin(), BB,
std::next(MachineBasicBlock::iterator(MI)), BB->end());
- for (MachineInstr *MovedMI : MovedInstrs)
- Observer.changedInstr(*MovedMI);
+ for (MachineInstr &MovedMI : DoneMBB->instrs())
+ Observer.changedInstr(MovedMI);
DoneMBB->transferSuccessorsAndUpdatePHIs(BB);
BB->addSuccessor(LoopMBB);
More information about the llvm-commits
mailing list