[llvm] [RISCV][GlobalISel] Legalize readcyclecounter/readsteadycounter (PR #217535)

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 02:35:45 PDT 2026


================
@@ -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);
----------------
arsenm wrote:

If you're emitting real target instructions, you might as well use BuildMI directly 

https://github.com/llvm/llvm-project/pull/217535


More information about the llvm-commits mailing list