[llvm] [Hexagon] Prevent optimizing out counter reads (PR #182338)

Alexey Karyakin via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 19 14:50:18 PST 2026


https://github.com/quic-akaryaki updated https://github.com/llvm/llvm-project/pull/182338

>From 05b6eb789eb1280c3c20f3e476ce68abd784e32a Mon Sep 17 00:00:00 2001
From: Alexey Karyakin <akaryaki at qti.qualcomm.com>
Date: Thu, 19 Feb 2026 09:17:13 -0800
Subject: [PATCH 1/2] [Hexagon] Tests for counter reads optmizations

---
 llvm/test/CodeGen/Hexagon/readcyclecounter.ll  | 17 +++++++++++++++++
 llvm/test/CodeGen/Hexagon/readsteadycounter.ll | 17 +++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/llvm/test/CodeGen/Hexagon/readcyclecounter.ll b/llvm/test/CodeGen/Hexagon/readcyclecounter.ll
index b1b4696641dee..e295412113f3f 100644
--- a/llvm/test/CodeGen/Hexagon/readcyclecounter.ll
+++ b/llvm/test/CodeGen/Hexagon/readcyclecounter.ll
@@ -7,4 +7,21 @@ define i64 @test_readcyclecounter() nounwind {
   ret i64 %t0
 }
 
+;; Make sure readcyclecounter calls are not optmized out and
+;; not moved across function calls.
+
+; CHECK-LABEL: test_readcyclecounter2
+; CHECK: [[R1:r[0-9:]+]] = c15:14
+; CHECK: call fun
+; CHECK: [[R2:r[0-9:]+]] = c15:14
+; CHECK: [[R3:r[0-9:]+]] = sub([[R2]],[[R1]])
+define i64 @test_readcyclecounter2() {
+  %1 = tail call i64 @llvm.readcyclecounter()
+  tail call void @fun()
+  %2 = tail call i64 @llvm.readcyclecounter()
+  %sub = sub i64 %2, %1
+  ret i64 %sub
+}
+
 declare i64 @llvm.readcyclecounter()
+declare void @fun()
diff --git a/llvm/test/CodeGen/Hexagon/readsteadycounter.ll b/llvm/test/CodeGen/Hexagon/readsteadycounter.ll
index 377097a99132b..b686f11263eb0 100644
--- a/llvm/test/CodeGen/Hexagon/readsteadycounter.ll
+++ b/llvm/test/CodeGen/Hexagon/readsteadycounter.ll
@@ -8,4 +8,21 @@ define i64 @test_readsteadycounter() nounwind {
   ret i64 %t0
 }
 
+;; Make sure readsteadycounter calls are not optmized out and
+;; not moved across function calls.
+
+; CHECK-LABEL: test_readsteadycounter2
+; CHECK: [[R1:r[0-9:]+]] = c31:30
+; CHECK: call fun
+; CHECK: [[R2:r[0-9:]+]] = c31:30
+; CHECK: [[R3:r[0-9:]+]] = sub([[R2]],[[R1]])
+define i64 @test_readsteadycounter2() {
+  %1 = tail call i64 @llvm.readsteadycounter()
+  tail call void @fun()
+  %2 = tail call i64 @llvm.readsteadycounter()
+  %sub = sub i64 %2, %1
+  ret i64 %sub
+}
+
 declare i64 @llvm.readsteadycounter()
+declare void @fun()

>From f4c5e8966cf3de33d68c4a0e5de56851dcee2f94 Mon Sep 17 00:00:00 2001
From: Alexey Karyakin <akaryaki at qti.qualcomm.com>
Date: Thu, 19 Feb 2026 09:18:28 -0800
Subject: [PATCH 2/2] [Hexagon] Prevent optimizing out counter reads

On Hexagon, ISD::READCYCLECOUNTER and ISD::READSTEADYCOUNTER are lowered
to the A2_tfrcpp instruction which reads the UPCYCLE (c15:14) and
UTIMER (c31:30) registers. Since these registers are volatile, compiler
should not optimize out or move these calls. Usually, this is achieved
by declaring the instruction as having side effects (e.g. MRS in
AArch64). However, as the same instruction is used to read other control
registers, and some of them have normal read/write semantics, it is not
desirable to inhibit such optimization for all of registers.
Additionally, pattern-based lowering of cycle counters is not allowed
by tablegen because of the mismatch in the hasSideEffects attribute.
Previously, this was worked around with custom lowering and
a Hexagon-specific opcode for each operation.

Therefore, reading UPCYCLE and UTIMER is now done through a
pseudoinstruction, which has the hasSideEffects attribute and is lowered
by a pattern. This allows us to easily separate control registers with
and without side effects. Two pseudoinsructions are defined:
PS_readcr (32 bits) and PS_readcr64 (64-bits). I also remove the custom
lowering and Hexagon-specific opcodes.

In case the compiler needs to emit code reading other counters, this
method can be used by simply adding adding patterns that use PS_readcr
or PS_readcr64.
---
 llvm/lib/Target/Hexagon/HexagonAsmPrinter.cpp |  8 +++++
 .../Target/Hexagon/HexagonISelLowering.cpp    | 35 +++----------------
 llvm/lib/Target/Hexagon/HexagonISelLowering.h |  2 --
 llvm/lib/Target/Hexagon/HexagonPatterns.td    | 13 ++-----
 llvm/lib/Target/Hexagon/HexagonPseudo.td      | 10 ++++++
 5 files changed, 26 insertions(+), 42 deletions(-)

diff --git a/llvm/lib/Target/Hexagon/HexagonAsmPrinter.cpp b/llvm/lib/Target/Hexagon/HexagonAsmPrinter.cpp
index f22852d1ef557..37a44d6033b84 100644
--- a/llvm/lib/Target/Hexagon/HexagonAsmPrinter.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonAsmPrinter.cpp
@@ -422,6 +422,14 @@ void HexagonAsmPrinter::HexagonProcessInstruction(MCInst &Inst,
     Inst.setOpcode(Hexagon::J2_call);
     break;
 
+  case Hexagon::PS_readcr:
+    Inst.setOpcode(Hexagon::A2_tfrcrr);
+    break;
+
+  case Hexagon::PS_readcr64:
+    Inst.setOpcode(Hexagon::A4_tfrcpp);
+    break;
+
   case Hexagon::S5_asrhub_rnd_sat_goodsyntax: {
     MCOperand &MO = MappedInst.getOperand(2);
     int64_t Imm;
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
index 4913e96cd3f0f..3f2c42c84e19c 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
@@ -754,32 +754,8 @@ SDValue HexagonTargetLowering::LowerPREFETCH(SDValue Op,
   return DAG.getNode(HexagonISD::DCFETCH, DL, MVT::Other, Chain, Addr, Zero);
 }
 
-// Custom-handle ISD::READCYCLECOUNTER because the target-independent SDNode
-// is marked as having side-effects, while the register read on Hexagon does
-// not have any. TableGen refuses to accept the direct pattern from that node
-// to the A4_tfrcpp.
-SDValue HexagonTargetLowering::LowerREADCYCLECOUNTER(SDValue Op,
-                                                     SelectionDAG &DAG) const {
-  SDValue Chain = Op.getOperand(0);
-  SDLoc dl(Op);
-  SDVTList VTs = DAG.getVTList(MVT::i64, MVT::Other);
-  return DAG.getNode(HexagonISD::READCYCLE, dl, VTs, Chain);
-}
-
-// Custom-handle ISD::READSTEADYCOUNTER because the target-independent SDNode
-// is marked as having side-effects, while the register read on Hexagon does
-// not have any. TableGen refuses to accept the direct pattern from that node
-// to the A4_tfrcpp.
-SDValue HexagonTargetLowering::LowerREADSTEADYCOUNTER(SDValue Op,
-                                                      SelectionDAG &DAG) const {
-  SDValue Chain = Op.getOperand(0);
-  SDLoc dl(Op);
-  SDVTList VTs = DAG.getVTList(MVT::i64, MVT::Other);
-  return DAG.getNode(HexagonISD::READTIMER, dl, VTs, Chain);
-}
-
 SDValue HexagonTargetLowering::LowerINTRINSIC_VOID(SDValue Op,
-      SelectionDAG &DAG) const {
+                                                   SelectionDAG &DAG) const {
   SDValue Chain = Op.getOperand(0);
   unsigned IntNo = Op.getConstantOperandVal(1);
   // Lower the hexagon_prefetch builtin to DCFETCH, as above.
@@ -1551,8 +1527,8 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
   setOperationAction(ISD::INLINEASM,            MVT::Other, Custom);
   setOperationAction(ISD::INLINEASM_BR,         MVT::Other, Custom);
   setOperationAction(ISD::PREFETCH,             MVT::Other, Custom);
-  setOperationAction(ISD::READCYCLECOUNTER,     MVT::i64,   Custom);
-  setOperationAction(ISD::READSTEADYCOUNTER,    MVT::i64,   Custom);
+  setOperationAction(ISD::READCYCLECOUNTER, MVT::i64, Legal);
+  setOperationAction(ISD::READSTEADYCOUNTER, MVT::i64, Legal);
   setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
   setOperationAction(ISD::INTRINSIC_VOID,       MVT::Other, Custom);
   setOperationAction(ISD::EH_RETURN,            MVT::Other, Custom);
@@ -3342,9 +3318,8 @@ HexagonTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
     case ISD::VSELECT:              return LowerVSELECT(Op, DAG);
     case ISD::INTRINSIC_WO_CHAIN:   return LowerINTRINSIC_WO_CHAIN(Op, DAG);
     case ISD::INTRINSIC_VOID:       return LowerINTRINSIC_VOID(Op, DAG);
-    case ISD::PREFETCH:             return LowerPREFETCH(Op, DAG);
-    case ISD::READCYCLECOUNTER:     return LowerREADCYCLECOUNTER(Op, DAG);
-    case ISD::READSTEADYCOUNTER:    return LowerREADSTEADYCOUNTER(Op, DAG);
+    case ISD::PREFETCH:
+      return LowerPREFETCH(Op, DAG);
       break;
   }
 
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index 1fbe404a82c5e..e535ceecaa5d4 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -120,8 +120,6 @@ class HexagonTargetLowering : public TargetLowering {
   SDValue LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFDIV(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerPREFETCH(SDValue Op, SelectionDAG &DAG) const;
-  SDValue LowerREADCYCLECOUNTER(SDValue Op, SelectionDAG &DAG) const;
-  SDValue LowerREADSTEADYCOUNTER(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerEH_LABEL(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const;
   SDValue
diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td
index ca2704978df79..6f4dccac45728 100644
--- a/llvm/lib/Target/Hexagon/HexagonPatterns.td
+++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td
@@ -3460,17 +3460,10 @@ def: Pat<(trap), (PS_crash)>;
 def: Pat<(debugtrap), (Y2_break)>;
 
 // Read cycle counter.
-def SDTInt64Leaf: SDTypeProfile<1, 0, [SDTCisVT<0, i64>]>;
-def HexagonREADCYCLE: SDNode<"HexagonISD::READCYCLE", SDTInt64Leaf,
-  [SDNPHasChain]>;
+def : Pat<(i64 (readcyclecounter)), (PS_readcr64 UPCYCLE)>;
 
-def: Pat<(HexagonREADCYCLE), (A4_tfrcpp UPCYCLE)>;
-
-// Read time counter.
-def HexagonREADTIMER: SDNode<"HexagonISD::READTIMER", SDTInt64Leaf,
-  [SDNPHasChain]>;
-
-def: Pat<(HexagonREADTIMER), (A4_tfrcpp UTIMER)>;
+// Read packet counter
+def : Pat<(i64 (readsteadycounter)), (PS_readcr64 UTIMER)>;
 
 def SDTInt32Leaf : SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>;
 def HexagonTHREADPOINTER : SDNode<"HexagonISD::THREAD_POINTER", SDTPtrLeaf>;
diff --git a/llvm/lib/Target/Hexagon/HexagonPseudo.td b/llvm/lib/Target/Hexagon/HexagonPseudo.td
index c2f38a9ccf5b3..4e5e8c3d26f00 100644
--- a/llvm/lib/Target/Hexagon/HexagonPseudo.td
+++ b/llvm/lib/Target/Hexagon/HexagonPseudo.td
@@ -349,6 +349,16 @@ def LDriw_ctr : LDInst<(outs CtrRegs:$dst),
                         (ins IntRegs:$addr, s32_0Imm:$off),
                         ".error \"should not emit\"", []>;
 
+let isCodeGenOnly = 1, isPseudo = 1, hasSideEffects = 1 in {
+  def PS_readcr : InstHexagon<(outs IntRegs:$Rd32), (ins CtrRegs:$Cs32),
+                              ".error \"should not emit\" ", [], "",
+                              A2_tfrcrr.Itinerary, TypeCR>;
+
+  def PS_readcr64
+      : InstHexagon<(outs DoubleRegs:$Rdd32), (ins CtrRegs64:$Css32),
+                    ".error \"should not emit\" ", [], "", A4_tfrcpp.Itinerary,
+                    TypeCR>;
+}
 
 let isCodeGenOnly = 1, isPseudo = 1 in
 def PS_pselect: InstHexagon<(outs DoubleRegs:$Rd),



More information about the llvm-commits mailing list