Skip to content

[hexagon] Add support for llvm.thread.pointer #148752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 15, 2025

Conversation

androm3da
Copy link
Member

@androm3da androm3da commented Jul 15, 2025

UGP contains the pointer for thread data:

The TLS area is accessed at the processor level through the special register UGP This register is set to the address one location above the TLS area, which grows downwards from UGP.

From the Hexagon ABI spec - https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf

Also: disable clang-format for NodeType enum in llvm/lib/Target/Hexagon/HexagonISelLowering.h to avoid disruptive formatting.

@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2025

@llvm/pr-subscribers-backend-hexagon

Author: Brian Cain (androm3da)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/148752.diff

4 Files Affected:

  • (modified) llvm/lib/Target/Hexagon/HexagonISelLowering.cpp (+13-1)
  • (modified) llvm/lib/Target/Hexagon/HexagonISelLowering.h (+1)
  • (modified) llvm/lib/Target/Hexagon/HexagonPatterns.td (+5)
  • (added) llvm/test/CodeGen/Hexagon/thread-pointer.ll (+16)
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
index ec73e58ce5d44..facea646d4b68 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp
@@ -236,7 +236,16 @@ MVT HexagonTargetLowering::getRegisterTypeForCallingConv(LLVMContext &Context,
 SDValue
 HexagonTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG)
       const {
-  return SDValue();
+  unsigned IntNo = Op.getConstantOperandVal(0);
+  SDLoc dl(Op);
+  switch (IntNo) {
+  default:
+    return SDValue(); // Don't custom lower most intrinsics.
+  case Intrinsic::thread_pointer: {
+    EVT PtrVT = getPointerTy(DAG.getDataLayout());
+    return DAG.getNode(HexagonISD::THREAD_POINTER, dl, PtrVT);
+  }
+  }
 }
 
 /// CreateCopyOfByValArgument - Make a copy of an aggregate at address specified
@@ -1588,6 +1597,7 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM,
   setOperationAction(ISD::PREFETCH,             MVT::Other, Custom);
   setOperationAction(ISD::READCYCLECOUNTER,     MVT::i64,   Custom);
   setOperationAction(ISD::READSTEADYCOUNTER,    MVT::i64,   Custom);
+  setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
   setOperationAction(ISD::INTRINSIC_VOID,       MVT::Other, Custom);
   setOperationAction(ISD::EH_RETURN,            MVT::Other, Custom);
   setOperationAction(ISD::GLOBAL_OFFSET_TABLE,  MVT::i32,   Custom);
@@ -1963,6 +1973,8 @@ const char* HexagonTargetLowering::getTargetNodeName(unsigned Opcode) const {
   case HexagonISD::VROR:          return "HexagonISD::VROR";
   case HexagonISD::READCYCLE:     return "HexagonISD::READCYCLE";
   case HexagonISD::READTIMER:     return "HexagonISD::READTIMER";
+  case HexagonISD::THREAD_POINTER:
+    return "HexagonISD::THREAD_POINTER";
   case HexagonISD::PTRUE:         return "HexagonISD::PTRUE";
   case HexagonISD::PFALSE:        return "HexagonISD::PFALSE";
   case HexagonISD::D2P:           return "HexagonISD::D2P";
diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index f9e5478f457f8..3d329520d097a 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -78,6 +78,7 @@ enum NodeType : unsigned {
   DCFETCH,
   READCYCLE,
   READTIMER,
+  THREAD_POINTER,
   PTRUE,
   PFALSE,
   D2P,         // Convert 8-byte value to 8-bit predicate register. [*]
diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td
index 82d999ad820ed..9b072bbacdbed 100644
--- a/llvm/lib/Target/Hexagon/HexagonPatterns.td
+++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td
@@ -3432,6 +3432,11 @@ def HexagonREADTIMER: SDNode<"HexagonISD::READTIMER", SDTInt64Leaf,
 
 def: Pat<(HexagonREADTIMER), (A4_tfrcpp UTIMER)>;
 
+def SDTInt32Leaf : SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>;
+def HexagonTHREADPOINTER : SDNode<"HexagonISD::THREAD_POINTER", SDTInt32Leaf>;
+
+def : Pat<(HexagonTHREADPOINTER), (i32(COPY UGP))>;
+
 // The declared return value of the store-locked intrinsics is i32, but
 // the instructions actually define i1. To avoid register copies from
 // IntRegs to PredRegs and back, fold the entire pattern checking the
diff --git a/llvm/test/CodeGen/Hexagon/thread-pointer.ll b/llvm/test/CodeGen/Hexagon/thread-pointer.ll
new file mode 100644
index 0000000000000..cebb9ff3e202d
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/thread-pointer.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=hexagon < %s | FileCheck %s
+;
+; This test verifies the thread pointer intrinsic implementation for Hexagon.
+; The thread pointer (UGP register) is used to access thread-local storage.
+
+declare ptr @llvm.thread.pointer() nounwind readnone
+
+define ptr @thread_pointer() nounwind {
+; CHECK-LABEL: thread_pointer:
+; CHECK: // %bb.0:
+; CHECK:         r0 = ugp
+; CHECK-NEXT:    jumpr r31
+  %1 = tail call ptr @llvm.thread.pointer()
+  ret ptr %1
+}

Copy link

github-actions bot commented Jul 15, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@androm3da
Copy link
Member Author

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
View the diff from clang-format here.

diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index 3d329520d..455e96527 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -35,37 +35,37 @@ enum NodeType : unsigned {
   OP_BEGIN = ISD::BUILTIN_OP_END,
 
   CONST32 = OP_BEGIN,
-  CONST32_GP,  // For marking data present in GP.
-  ADDC,        // Add with carry: (X, Y, Cin) -> (X+Y, Cout).
-  SUBC,        // Sub with carry: (X, Y, Cin) -> (X+~Y+Cin, Cout).
+  CONST32_GP, // For marking data present in GP.
+  ADDC,       // Add with carry: (X, Y, Cin) -> (X+Y, Cout).
+  SUBC,       // Sub with carry: (X, Y, Cin) -> (X+~Y+Cin, Cout).
   ALLOCA,
 
-  AT_GOT,      // Index in GOT.
-  AT_PCREL,    // Offset relative to PC.
+  AT_GOT,   // Index in GOT.
+  AT_PCREL, // Offset relative to PC.
 
-  CALL,        // Function call.
-  CALLnr,      // Function call that does not return.
+  CALL,   // Function call.
+  CALLnr, // Function call that does not return.
   CALLR,
 
-  RET_GLUE,    // Return with a glue operand.
-  BARRIER,     // Memory barrier.
-  JT,          // Jump table.
-  CP,          // Constant pool.
+  RET_GLUE, // Return with a glue operand.
+  BARRIER,  // Memory barrier.
+  JT,       // Jump table.
+  CP,       // Constant pool.
 
   COMBINE,
-  VASL,        // Vector shifts by a scalar value
+  VASL, // Vector shifts by a scalar value
   VASR,
   VLSR,
-  MFSHL,       // Funnel shifts with the shift amount guaranteed to be
-  MFSHR,       // within the range of the bit width of the element.
+  MFSHL, // Funnel shifts with the shift amount guaranteed to be
+  MFSHR, // within the range of the bit width of the element.
 
-  SSAT,        // Signed saturate.
-  USAT,        // Unsigned saturate.
-  SMUL_LOHI,   // Same as ISD::SMUL_LOHI, but opaque to the combiner.
-  UMUL_LOHI,   // Same as ISD::UMUL_LOHI, but opaque to the combiner.
-               // We want to legalize MULH[SU] to [SU]MUL_LOHI, but the
-               // combiner will keep rewriting it back to MULH[SU].
-  USMUL_LOHI,  // Like SMUL_LOHI, but unsigned*signed.
+  SSAT,       // Signed saturate.
+  USAT,       // Unsigned saturate.
+  SMUL_LOHI,  // Same as ISD::SMUL_LOHI, but opaque to the combiner.
+  UMUL_LOHI,  // Same as ISD::UMUL_LOHI, but opaque to the combiner.
+              // We want to legalize MULH[SU] to [SU]MUL_LOHI, but the
+              // combiner will keep rewriting it back to MULH[SU].
+  USMUL_LOHI, // Like SMUL_LOHI, but unsigned*signed.
 
   TSTBIT,
   INSERT,
@@ -81,13 +81,13 @@ enum NodeType : unsigned {
   THREAD_POINTER,
   PTRUE,
   PFALSE,
-  D2P,         // Convert 8-byte value to 8-bit predicate register. [*]
-  P2D,         // Convert 8-bit predicate register to 8-byte value. [*]
-  V2Q,         // Convert HVX vector to a vector predicate reg. [*]
-  Q2V,         // Convert vector predicate to an HVX vector. [*]
-               // [*] The equivalence is defined as "Q <=> (V != 0)",
-               //     where the != operation compares bytes.
-               // Note: V != 0 is implemented as V >u 0.
+  D2P, // Convert 8-byte value to 8-bit predicate register. [*]
+  P2D, // Convert 8-bit predicate register to 8-byte value. [*]
+  V2Q, // Convert HVX vector to a vector predicate reg. [*]
+  Q2V, // Convert vector predicate to an HVX vector. [*]
+       // [*] The equivalence is defined as "Q <=> (V != 0)",
+       //     where the != operation compares bytes.
+       // Note: V != 0 is implemented as V >u 0.
   QCAT,
   QTRUE,
   QFALSE,
@@ -109,15 +109,15 @@ enum NodeType : unsigned {
                // are called again after everything else is legal, giving
                // us the opportunity to undo the wrapping.
 
-  TYPECAST,    // No-op that's used to convert between different legal
-               // types in a register.
-  VALIGN,      // Align two vectors (in Op0, Op1) to one that would have
-               // been loaded from address in Op2.
-  VALIGNADDR,  // Align vector address: Op0 & -Op1, except when it is
-               // an address in a vector load, then it's a no-op.
-  ISEL,        // Marker for nodes that were created during ISel, and
-               // which need explicit selection (would have been left
-               // unselected otherwise).
+  TYPECAST,   // No-op that's used to convert between different legal
+              // types in a register.
+  VALIGN,     // Align two vectors (in Op0, Op1) to one that would have
+              // been loaded from address in Op2.
+  VALIGNADDR, // Align vector address: Op0 & -Op1, except when it is
+              // an address in a vector load, then it's a no-op.
+  ISEL,       // Marker for nodes that were created during ISel, and
+              // which need explicit selection (would have been left
+              // unselected otherwise).
   OP_END
 };
 

I deliberately un-applied this clang-format because it captured so much unrelated content.

Copy link
Contributor

@aankit-ca aankit-ca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -3432,6 +3432,11 @@ def HexagonREADTIMER: SDNode<"HexagonISD::READTIMER", SDTInt64Leaf,

def: Pat<(HexagonREADTIMER), (A4_tfrcpp UTIMER)>;

def SDTInt32Leaf : SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>;
def HexagonTHREADPOINTER : SDNode<"HexagonISD::THREAD_POINTER", SDTInt32Leaf>;
Copy link
Contributor

@aankit-ca aankit-ca Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you use a ptr return type here?

Suggested change
def HexagonTHREADPOINTER : SDNode<"HexagonISD::THREAD_POINTER", SDTInt32Leaf>;
def HexagonTHREADPOINTER : SDNode<"HexagonISD::THREAD_POINTER", SDTPtrLeaf>;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah thanks - good catch.

@aankit-ca
Copy link
Contributor

aankit-ca commented Jul 15, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️
You can test this locally with the following command:
View the diff from clang-format here.

diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.h b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
index 3d329520d..455e96527 100644
--- a/llvm/lib/Target/Hexagon/HexagonISelLowering.h
+++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.h
@@ -35,37 +35,37 @@ enum NodeType : unsigned {
   OP_BEGIN = ISD::BUILTIN_OP_END,
 
   CONST32 = OP_BEGIN,
-  CONST32_GP,  // For marking data present in GP.
-  ADDC,        // Add with carry: (X, Y, Cin) -> (X+Y, Cout).
-  SUBC,        // Sub with carry: (X, Y, Cin) -> (X+~Y+Cin, Cout).
+  CONST32_GP, // For marking data present in GP.
+  ADDC,       // Add with carry: (X, Y, Cin) -> (X+Y, Cout).
+  SUBC,       // Sub with carry: (X, Y, Cin) -> (X+~Y+Cin, Cout).
   ALLOCA,
 
-  AT_GOT,      // Index in GOT.
-  AT_PCREL,    // Offset relative to PC.
+  AT_GOT,   // Index in GOT.
+  AT_PCREL, // Offset relative to PC.
 
-  CALL,        // Function call.
-  CALLnr,      // Function call that does not return.
+  CALL,   // Function call.
+  CALLnr, // Function call that does not return.
   CALLR,
 
-  RET_GLUE,    // Return with a glue operand.
-  BARRIER,     // Memory barrier.
-  JT,          // Jump table.
-  CP,          // Constant pool.
+  RET_GLUE, // Return with a glue operand.
+  BARRIER,  // Memory barrier.
+  JT,       // Jump table.
+  CP,       // Constant pool.
 
   COMBINE,
-  VASL,        // Vector shifts by a scalar value
+  VASL, // Vector shifts by a scalar value
   VASR,
   VLSR,
-  MFSHL,       // Funnel shifts with the shift amount guaranteed to be
-  MFSHR,       // within the range of the bit width of the element.
+  MFSHL, // Funnel shifts with the shift amount guaranteed to be
+  MFSHR, // within the range of the bit width of the element.
 
-  SSAT,        // Signed saturate.
-  USAT,        // Unsigned saturate.
-  SMUL_LOHI,   // Same as ISD::SMUL_LOHI, but opaque to the combiner.
-  UMUL_LOHI,   // Same as ISD::UMUL_LOHI, but opaque to the combiner.
-               // We want to legalize MULH[SU] to [SU]MUL_LOHI, but the
-               // combiner will keep rewriting it back to MULH[SU].
-  USMUL_LOHI,  // Like SMUL_LOHI, but unsigned*signed.
+  SSAT,       // Signed saturate.
+  USAT,       // Unsigned saturate.
+  SMUL_LOHI,  // Same as ISD::SMUL_LOHI, but opaque to the combiner.
+  UMUL_LOHI,  // Same as ISD::UMUL_LOHI, but opaque to the combiner.
+              // We want to legalize MULH[SU] to [SU]MUL_LOHI, but the
+              // combiner will keep rewriting it back to MULH[SU].
+  USMUL_LOHI, // Like SMUL_LOHI, but unsigned*signed.
 
   TSTBIT,
   INSERT,
@@ -81,13 +81,13 @@ enum NodeType : unsigned {
   THREAD_POINTER,
   PTRUE,
   PFALSE,
-  D2P,         // Convert 8-byte value to 8-bit predicate register. [*]
-  P2D,         // Convert 8-bit predicate register to 8-byte value. [*]
-  V2Q,         // Convert HVX vector to a vector predicate reg. [*]
-  Q2V,         // Convert vector predicate to an HVX vector. [*]
-               // [*] The equivalence is defined as "Q <=> (V != 0)",
-               //     where the != operation compares bytes.
-               // Note: V != 0 is implemented as V >u 0.
+  D2P, // Convert 8-byte value to 8-bit predicate register. [*]
+  P2D, // Convert 8-bit predicate register to 8-byte value. [*]
+  V2Q, // Convert HVX vector to a vector predicate reg. [*]
+  Q2V, // Convert vector predicate to an HVX vector. [*]
+       // [*] The equivalence is defined as "Q <=> (V != 0)",
+       //     where the != operation compares bytes.
+       // Note: V != 0 is implemented as V >u 0.
   QCAT,
   QTRUE,
   QFALSE,
@@ -109,15 +109,15 @@ enum NodeType : unsigned {
                // are called again after everything else is legal, giving
                // us the opportunity to undo the wrapping.
 
-  TYPECAST,    // No-op that's used to convert between different legal
-               // types in a register.
-  VALIGN,      // Align two vectors (in Op0, Op1) to one that would have
-               // been loaded from address in Op2.
-  VALIGNADDR,  // Align vector address: Op0 & -Op1, except when it is
-               // an address in a vector load, then it's a no-op.
-  ISEL,        // Marker for nodes that were created during ISel, and
-               // which need explicit selection (would have been left
-               // unselected otherwise).
+  TYPECAST,   // No-op that's used to convert between different legal
+              // types in a register.
+  VALIGN,     // Align two vectors (in Op0, Op1) to one that would have
+              // been loaded from address in Op2.
+  VALIGNADDR, // Align vector address: Op0 & -Op1, except when it is
+              // an address in a vector load, then it's a no-op.
+  ISEL,       // Marker for nodes that were created during ISel, and
+              // which need explicit selection (would have been left
+              // unselected otherwise).
   OP_END
 };
 

I deliberately un-applied this clang-format because it captured so much unrelated content.

@androm3da Better to turn off clang-format for this enum

@androm3da androm3da merged commit 2b95275 into llvm:main Jul 15, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants