Skip to content

Commit b8ed69e

Browse files
committed
[RISCV] Support llvm.readsteadycounter intrinsic
This intrinsic was introduced by llvm#81331, which is a lot like `llvm.readcyclecounter`. For the RISCV implementation, we rename `ReadCycleWide` pseudo to `ReadCounterWide` and make it accept two operands (the low and high parts of the counter). As for legalization and lowering parts, we reuse the code of `ISD::READCYCLECOUNTER` (make it able to handle both intrinsics), and we use `time` CSR for `ISD::READSTEADYCOUNTER`. Tests using Clang builtins are runned on real hardware and it works as excepted. Reviewers: asb, MaskRay, dtcxzyw, preames, topperc, jhuber6 Reviewed By: jhuber6, asb, MaskRay, dtcxzyw Pull Request: llvm#82322
1 parent e4057aa commit b8ed69e

File tree

4 files changed

+97
-42
lines changed

4 files changed

+97
-42
lines changed

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -625,10 +625,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
625625
if (Subtarget.is64Bit())
626626
setOperationAction(ISD::Constant, MVT::i64, Custom);
627627

628-
// TODO: On M-mode only targets, the cycle[h] CSR may not be present.
628+
// TODO: On M-mode only targets, the cycle[h]/time[h] CSR may not be present.
629629
// Unfortunately this can't be determined just from the ISA naming string.
630630
setOperationAction(ISD::READCYCLECOUNTER, MVT::i64,
631631
Subtarget.is64Bit() ? Legal : Custom);
632+
setOperationAction(ISD::READSTEADYCOUNTER, MVT::i64,
633+
Subtarget.is64Bit() ? Legal : Custom);
632634

633635
setOperationAction({ISD::TRAP, ISD::DEBUGTRAP}, MVT::Other, Legal);
634636
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
@@ -11724,13 +11726,27 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
1172411726
Results.push_back(Result);
1172511727
break;
1172611728
}
11727-
case ISD::READCYCLECOUNTER: {
11728-
assert(!Subtarget.is64Bit() &&
11729-
"READCYCLECOUNTER only has custom type legalization on riscv32");
11729+
case ISD::READCYCLECOUNTER:
11730+
case ISD::READSTEADYCOUNTER: {
11731+
assert(!Subtarget.is64Bit() && "READCYCLECOUNTER/READSTEADYCOUNTER only "
11732+
"has custom type legalization on riscv32");
1173011733

11734+
SDValue LoCounter, HiCounter;
11735+
MVT XLenVT = Subtarget.getXLenVT();
11736+
if (N->getOpcode() == ISD::READCYCLECOUNTER) {
11737+
LoCounter = DAG.getConstant(
11738+
RISCVSysReg::lookupSysRegByName("CYCLE")->Encoding, DL, XLenVT);
11739+
HiCounter = DAG.getConstant(
11740+
RISCVSysReg::lookupSysRegByName("CYCLEH")->Encoding, DL, XLenVT);
11741+
} else {
11742+
LoCounter = DAG.getConstant(
11743+
RISCVSysReg::lookupSysRegByName("TIME")->Encoding, DL, XLenVT);
11744+
HiCounter = DAG.getConstant(
11745+
RISCVSysReg::lookupSysRegByName("TIMEH")->Encoding, DL, XLenVT);
11746+
}
1173111747
SDVTList VTs = DAG.getVTList(MVT::i32, MVT::i32, MVT::Other);
11732-
SDValue RCW =
11733-
DAG.getNode(RISCVISD::READ_CYCLE_WIDE, DL, VTs, N->getOperand(0));
11748+
SDValue RCW = DAG.getNode(RISCVISD::READ_COUNTER_WIDE, DL, VTs,
11749+
N->getOperand(0), LoCounter, HiCounter);
1173411750

1173511751
Results.push_back(
1173611752
DAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, RCW, RCW.getValue(1)));
@@ -16902,29 +16918,30 @@ RISCVTargetLowering::getTargetConstantFromLoad(LoadSDNode *Ld) const {
1690216918
return CNodeLo->getConstVal();
1690316919
}
1690416920

16905-
static MachineBasicBlock *emitReadCycleWidePseudo(MachineInstr &MI,
16906-
MachineBasicBlock *BB) {
16907-
assert(MI.getOpcode() == RISCV::ReadCycleWide && "Unexpected instruction");
16921+
static MachineBasicBlock *emitReadCounterWidePseudo(MachineInstr &MI,
16922+
MachineBasicBlock *BB) {
16923+
assert(MI.getOpcode() == RISCV::ReadCounterWide && "Unexpected instruction");
1690816924

16909-
// To read the 64-bit cycle CSR on a 32-bit target, we read the two halves.
16925+
// To read a 64-bit counter CSR on a 32-bit target, we read the two halves.
1691016926
// Should the count have wrapped while it was being read, we need to try
1691116927
// again.
16912-
// ...
16928+
// For example:
16929+
// ```
1691316930
// read:
16914-
// rdcycleh x3 # load high word of cycle
16915-
// rdcycle x2 # load low word of cycle
16916-
// rdcycleh x4 # load high word of cycle
16917-
// bne x3, x4, read # check if high word reads match, otherwise try again
16918-
// ...
16931+
// csrrs x3, counterh # load high word of counter
16932+
// csrrs x2, counter # load low word of counter
16933+
// csrrs x4, counterh # load high word of counter
16934+
// bne x3, x4, read # check if high word reads match, otherwise try again
16935+
// ```
1691916936

1692016937
MachineFunction &MF = *BB->getParent();
16921-
const BasicBlock *LLVM_BB = BB->getBasicBlock();
16938+
const BasicBlock *LLVMBB = BB->getBasicBlock();
1692216939
MachineFunction::iterator It = ++BB->getIterator();
1692316940

16924-
MachineBasicBlock *LoopMBB = MF.CreateMachineBasicBlock(LLVM_BB);
16941+
MachineBasicBlock *LoopMBB = MF.CreateMachineBasicBlock(LLVMBB);
1692516942
MF.insert(It, LoopMBB);
1692616943

16927-
MachineBasicBlock *DoneMBB = MF.CreateMachineBasicBlock(LLVM_BB);
16944+
MachineBasicBlock *DoneMBB = MF.CreateMachineBasicBlock(LLVMBB);
1692816945
MF.insert(It, DoneMBB);
1692916946

1693016947
// Transfer the remainder of BB and its successor edges to DoneMBB.
@@ -16938,17 +16955,19 @@ static MachineBasicBlock *emitReadCycleWidePseudo(MachineInstr &MI,
1693816955
Register ReadAgainReg = RegInfo.createVirtualRegister(&RISCV::GPRRegClass);
1693916956
Register LoReg = MI.getOperand(0).getReg();
1694016957
Register HiReg = MI.getOperand(1).getReg();
16958+
int64_t LoCounter = MI.getOperand(2).getImm();
16959+
int64_t HiCounter = MI.getOperand(3).getImm();
1694116960
DebugLoc DL = MI.getDebugLoc();
1694216961

1694316962
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
1694416963
BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), HiReg)
16945-
.addImm(RISCVSysReg::lookupSysRegByName("CYCLEH")->Encoding)
16964+
.addImm(HiCounter)
1694616965
.addReg(RISCV::X0);
1694716966
BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), LoReg)
16948-
.addImm(RISCVSysReg::lookupSysRegByName("CYCLE")->Encoding)
16967+
.addImm(LoCounter)
1694916968
.addReg(RISCV::X0);
1695016969
BuildMI(LoopMBB, DL, TII->get(RISCV::CSRRS), ReadAgainReg)
16951-
.addImm(RISCVSysReg::lookupSysRegByName("CYCLEH")->Encoding)
16970+
.addImm(HiCounter)
1695216971
.addReg(RISCV::X0);
1695316972

1695416973
BuildMI(LoopMBB, DL, TII->get(RISCV::BNE))
@@ -17527,10 +17546,10 @@ RISCVTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
1752717546
switch (MI.getOpcode()) {
1752817547
default:
1752917548
llvm_unreachable("Unexpected instr type to insert");
17530-
case RISCV::ReadCycleWide:
17549+
case RISCV::ReadCounterWide:
1753117550
assert(!Subtarget.is64Bit() &&
17532-
"ReadCycleWrite is only to be used on riscv32");
17533-
return emitReadCycleWidePseudo(MI, BB);
17551+
"ReadCounterWide is only to be used on riscv32");
17552+
return emitReadCounterWidePseudo(MI, BB);
1753417553
case RISCV::Select_GPR_Using_CC_GPR:
1753517554
case RISCV::Select_FPR16_Using_CC_GPR:
1753617555
case RISCV::Select_FPR16INX_Using_CC_GPR:
@@ -19202,7 +19221,7 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
1920219221
NODE_NAME_CASE(FCLASS)
1920319222
NODE_NAME_CASE(FMAX)
1920419223
NODE_NAME_CASE(FMIN)
19205-
NODE_NAME_CASE(READ_CYCLE_WIDE)
19224+
NODE_NAME_CASE(READ_COUNTER_WIDE)
1920619225
NODE_NAME_CASE(BREV8)
1920719226
NODE_NAME_CASE(ORC_B)
1920819227
NODE_NAME_CASE(ZIP)

llvm/lib/Target/RISCV/RISCVISelLowering.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,10 @@ enum NodeType : unsigned {
126126
// Floating point fmax and fmin matching the RISC-V instruction semantics.
127127
FMAX, FMIN,
128128

129-
// READ_CYCLE_WIDE - A read of the 64-bit cycle CSR on a 32-bit target
130-
// (returns (Lo, Hi)). It takes a chain operand.
131-
READ_CYCLE_WIDE,
129+
// A read of the 64-bit counter CSR on a 32-bit target (returns (Lo, Hi)).
130+
// It takes a chain operand.
131+
READ_COUNTER_WIDE,
132+
132133
// brev8, orc.b, zip, and unzip from Zbb and Zbkb. All operands are i32 or
133134
// XLenVT.
134135
BREV8,

llvm/lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ def SDT_RISCVReadCSR : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisInt<1>]>;
3333
def SDT_RISCVWriteCSR : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisInt<1>]>;
3434
def SDT_RISCVSwapCSR : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisInt<1>,
3535
SDTCisInt<2>]>;
36-
def SDT_RISCVReadCycleWide : SDTypeProfile<2, 0, [SDTCisVT<0, i32>,
37-
SDTCisVT<1, i32>]>;
36+
def SDT_RISCVReadCounterWide : SDTypeProfile<2, 2, [SDTCisVT<0, i32>,
37+
SDTCisVT<1, i32>,
38+
SDTCisInt<2>,
39+
SDTCisInt<3>]>;
3840
def SDT_RISCVIntUnaryOpW : SDTypeProfile<1, 1, [
3941
SDTCisSameAs<0, 1>, SDTCisVT<0, i64>
4042
]>;
@@ -77,9 +79,9 @@ def riscv_write_csr : SDNode<"RISCVISD::WRITE_CSR", SDT_RISCVWriteCSR,
7779
def riscv_swap_csr : SDNode<"RISCVISD::SWAP_CSR", SDT_RISCVSwapCSR,
7880
[SDNPHasChain]>;
7981

80-
def riscv_read_cycle_wide : SDNode<"RISCVISD::READ_CYCLE_WIDE",
81-
SDT_RISCVReadCycleWide,
82-
[SDNPHasChain, SDNPSideEffect]>;
82+
def riscv_read_counter_wide : SDNode<"RISCVISD::READ_COUNTER_WIDE",
83+
SDT_RISCVReadCounterWide,
84+
[SDNPHasChain, SDNPSideEffect]>;
8385

8486
def riscv_add_lo : SDNode<"RISCVISD::ADD_LO", SDTIntBinOp>;
8587
def riscv_hi : SDNode<"RISCVISD::HI", SDTIntUnaryOp>;
@@ -363,7 +365,7 @@ def CSRSystemRegister : AsmOperandClass {
363365
let DiagnosticType = "InvalidCSRSystemRegister";
364366
}
365367

366-
def csr_sysreg : RISCVOp {
368+
def csr_sysreg : RISCVOp, ImmLeaf<XLenVT, "return isUInt<12>(Imm);"> {
367369
let ParserMatchClass = CSRSystemRegister;
368370
let PrintMethod = "printCSRSystemRegister";
369371
let DecoderMethod = "decodeUImmOperand<12>";
@@ -1827,16 +1829,21 @@ def : StPat<truncstorei32, SW, GPR, i64>;
18271829
def : StPat<store, SD, GPR, i64>;
18281830
} // Predicates = [IsRV64]
18291831

1832+
// On RV64, we can directly read these 64-bit counter CSRs.
1833+
let Predicates = [IsRV64] in {
18301834
/// readcyclecounter
1831-
// On RV64, we can directly read the 64-bit "cycle" CSR.
1832-
let Predicates = [IsRV64] in
18331835
def : Pat<(i64 (readcyclecounter)), (CSRRS CYCLE.Encoding, (XLenVT X0))>;
1834-
// On RV32, ReadCycleWide will be expanded to the suggested loop reading both
1835-
// halves of the 64-bit "cycle" CSR.
1836+
/// readsteadycounter
1837+
def : Pat<(i64 (readsteadycounter)), (CSRRS TIME.Encoding, (XLenVT X0))>;
1838+
}
1839+
1840+
// On RV32, ReadCounterWide will be expanded to the suggested loop reading both
1841+
// halves of 64-bit counter CSRs.
18361842
let Predicates = [IsRV32], usesCustomInserter = 1, hasNoSchedulingInfo = 1 in
1837-
def ReadCycleWide : Pseudo<(outs GPR:$lo, GPR:$hi), (ins),
1838-
[(set GPR:$lo, GPR:$hi, (riscv_read_cycle_wide))],
1839-
"", "">;
1843+
def ReadCounterWide : Pseudo<(outs GPR:$lo, GPR:$hi), (ins i32imm:$csr_lo, i32imm:$csr_hi),
1844+
[(set GPR:$lo, GPR:$hi,
1845+
(riscv_read_counter_wide csr_sysreg:$csr_lo, csr_sysreg:$csr_hi))],
1846+
"", "">;
18401847

18411848
/// traps
18421849

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
3+
; RUN: | FileCheck -check-prefix=RV32I %s
4+
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
5+
; RUN: | FileCheck -check-prefix=RV64I %s
6+
7+
; Verify that we lower @llvm.readsteadycounter() correctly.
8+
9+
declare i64 @llvm.readsteadycounter()
10+
11+
define i64 @test_builtin_readsteadycounter() nounwind {
12+
; RV32I-LABEL: test_builtin_readsteadycounter:
13+
; RV32I: # %bb.0:
14+
; RV32I-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1
15+
; RV32I-NEXT: rdtimeh a1
16+
; RV32I-NEXT: rdtime a0
17+
; RV32I-NEXT: rdtimeh a2
18+
; RV32I-NEXT: bne a1, a2, .LBB0_1
19+
; RV32I-NEXT: # %bb.2:
20+
; RV32I-NEXT: ret
21+
;
22+
; RV64I-LABEL: test_builtin_readsteadycounter:
23+
; RV64I: # %bb.0:
24+
; RV64I-NEXT: rdtime a0
25+
; RV64I-NEXT: ret
26+
%1 = tail call i64 @llvm.readsteadycounter()
27+
ret i64 %1
28+
}

0 commit comments

Comments
 (0)