https://github.com/moorabbit updated 
https://github.com/llvm/llvm-project/pull/148281

>From 029d9fce6cdb75ea4819a107c7ed9f074bb36678 Mon Sep 17 00:00:00 2001
From: moorabbit <215698969+moorab...@users.noreply.github.com>
Date: Mon, 7 Jul 2025 09:25:46 -0400
Subject: [PATCH 1/2] [Clang] Add `__builtin_stack_address`

Add support for `__builtin_stack_address` builtin. The semantics match those of
GCC's builtin with the same name.

`__builtin_stack_address` returns the starting address of the stack region that
may be used by called functions. This PR only adds support for the following
architectures: x86 - x86_64.  Support for other architectures can be added in
future patches.

Fixes #82632
---
 clang/docs/LanguageExtensions.rst             | 33 +++++++++++++++++
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/include/clang/Basic/Builtins.td         |  6 ++++
 clang/lib/CodeGen/CGBuiltin.cpp               |  4 +++
 clang/lib/Sema/SemaChecking.cpp               |  9 +++++
 clang/test/CodeGen/builtin-stackaddress.c     | 14 ++++++++
 .../test/CodeGenCXX/builtin-stackaddress.cpp  | 36 +++++++++++++++++++
 .../builtin-stackaddress-target-support.c     | 16 +++++++++
 clang/test/Sema/builtin-stackaddress.c        |  5 +++
 llvm/include/llvm/CodeGen/ISDOpcodes.h        |  5 +++
 llvm/include/llvm/IR/Intrinsics.td            |  1 +
 llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp |  1 +
 .../SelectionDAG/SelectionDAGBuilder.cpp      |  6 ++++
 .../SelectionDAG/SelectionDAGDumper.cpp       |  1 +
 llvm/lib/Target/X86/X86ISelLowering.cpp       |  8 +++++
 llvm/lib/Target/X86/X86ISelLowering.h         |  1 +
 16 files changed, 148 insertions(+)
 create mode 100644 clang/test/CodeGen/builtin-stackaddress.c
 create mode 100644 clang/test/CodeGenCXX/builtin-stackaddress.cpp
 create mode 100644 clang/test/Sema/builtin-stackaddress-target-support.c

diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index a42a546555716..5b78ae42559be 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -4189,6 +4189,39 @@ assignment can happen automatically.
 to a variable, have its address taken, or passed into or returned from a
 function, because doing so violates bounds safety conventions.
 
+.. _builtin_stack_address-doc:
+
+``__builtin_stack_address``
+---------------------------
+
+``__builtin_stack_address`` returns the address that separates the current
+function's (i.e. the one calling the builtin) stack space and the region of the
+stack that may be modified by called functions. The semantics match those of 
GCC's builtin of the same name.
+
+**Note:** Support for this builtin is currently limited to the following 
architectures: x86_64, x86.
+
+**Syntax**:
+
+.. code-block:: c++
+
+  void *__builtin_stack_address()
+
+**Example**:
+
+.. code-block:: c++
+
+  void *sp = __builtin_stack_address();
+
+**Description**:
+
+The address returned by ``__builtin_stack_address`` identifies the starting
+address of the stack region that may be used by called functions.
+
+On some architectures (e.g. x86), it's sufficient to return the value in the 
stack pointer register
+directly. On others (e.g. SPARCv9), adjustments are required to the value of 
the stack pointer
+register. ``__builtin_stack_address`` performs the necessary adjustments and 
returns the correct
+boundary address.
+
 Multiprecision Arithmetic Builtins
 ----------------------------------
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 57a94242c9e61..ccf83eb2f16fa 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -172,6 +172,8 @@ Resolutions to C++ Defect Reports
 C Language Changes
 ------------------
 
+- Clang now supports the :ref:`__builtin_stack_address 
<builtin_stack_address-doc>` () builtin.
+  The semantics match those of GCC's builtin with the same name.
 - Clang now allows an ``inline`` specifier on a typedef declaration of a
   function type in Microsoft compatibility mode. #GH124869
 - Clang now allows ``restrict`` qualifier for array types with pointer 
elements (#GH92847).
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 5ebb82180521d..f2012c813c9a7 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -917,6 +917,12 @@ def FrameAddress : Builtin {
   let Prototype = "void*(_Constant unsigned int)";
 }
 
+def StackAddress : Builtin {
+  let Spellings = ["__builtin_stack_address"];
+  let Attributes = [NoThrow];
+  let Prototype = "void*()";
+}
+
 def ClearCache : Builtin {
   let Spellings = ["__builtin___clear_cache"];
   let Attributes = [NoThrow];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 48c91eb4a5b4f..641bbede4bae7 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4673,6 +4673,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl 
GD, unsigned BuiltinID,
     Function *F = CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy);
     return RValue::get(Builder.CreateCall(F, Depth));
   }
+  case Builtin::BI__builtin_stack_address: {
+    return RValue::get(Builder.CreateCall(
+        CGM.getIntrinsic(Intrinsic::stackaddress, AllocaInt8PtrTy)));
+  }
   case Builtin::BI__builtin_extract_return_addr: {
     Value *Address = EmitScalarExpr(E->getArg(0));
     Value *Result = getTargetHooks().decodeReturnAddress(*this, Address);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index dd5b710d7e1d4..ca9371d6d2179 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2958,6 +2958,15 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
     break;
   }
 
+  case Builtin::BI__builtin_stack_address: {
+    if (CheckBuiltinTargetInSupported(
+            *this, TheCall,
+            /*SupportedArchs=*/{llvm::Triple::x86_64, llvm::Triple::x86})) {
+      return ExprError();
+    }
+    break;
+  }
+
   case Builtin::BI__builtin_nondeterministic_value: {
     if (BuiltinNonDeterministicValue(TheCall))
       return ExprError();
diff --git a/clang/test/CodeGen/builtin-stackaddress.c 
b/clang/test/CodeGen/builtin-stackaddress.c
new file mode 100644
index 0000000000000..a6b44b227947d
--- /dev/null
+++ b/clang/test/CodeGen/builtin-stackaddress.c
@@ -0,0 +1,14 @@
+// RUN: %clang -target x86_64 -S -emit-llvm %s -o - | FileCheck %s 
--check-prefix=llvm
+// RUN: %clang -target x86_64 -S %s -o - | FileCheck %s --check-prefix=x64
+
+extern void f(int, int, int, long, long, long, long, long, long, long, long);
+
+// llvm-LABEL: define {{[^@]+}} @a()
+// llvm:       call   {{[^@]+}} @llvm.stackaddress.p0()
+//
+// x64-LABEL: a:
+// x64:       movq  %rsp, %rax
+void *a() {
+  f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
+  return __builtin_stack_address();
+}
diff --git a/clang/test/CodeGenCXX/builtin-stackaddress.cpp 
b/clang/test/CodeGenCXX/builtin-stackaddress.cpp
new file mode 100644
index 0000000000000..24a949e83d9e0
--- /dev/null
+++ b/clang/test/CodeGenCXX/builtin-stackaddress.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang -target x86_64 -S -emit-llvm %s -o - | llvm-cxxfilt | FileCheck 
%s --check-prefix=llvm
+// RUN: %clang -target x86_64 -S %s -o - | llvm-cxxfilt | FileCheck %s 
--check-prefix=x64
+
+extern void f(int, int, int, long, long, long, long, long, long, long, long);
+
+struct S {
+  void *a();
+};
+
+// llvm-LABEL: define {{[^@]+}} @S::a()
+// llvm:       call   {{[^@]+}} @llvm.stackaddress.p0()
+//
+// x64-LABEL: S::a():
+// x64:       movq  %rsp, %rax
+void *S::a() {
+  void *p = __builtin_stack_address();
+  f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
+  return p;
+}
+
+// llvm-LABEL: define {{[^@]+}} @two()
+// llvm:       call   {{[^@]+}} @"two()::$_0::operator()() const"
+//
+// llvm-LABEL: define {{[^@]+}} @"two()::$_0::operator()() const"
+// llvm:       call   {{[^@]+}} @llvm.stackaddress.p0()
+//
+// x64-LABEL: two()::$_0::operator()() const:
+// x64:       movq  %rsp, %rax
+void *two() {
+  auto l = []() {
+    void *p = __builtin_stack_address();
+    f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
+    return p;
+  };
+  return l();
+}
diff --git a/clang/test/Sema/builtin-stackaddress-target-support.c 
b/clang/test/Sema/builtin-stackaddress-target-support.c
new file mode 100644
index 0000000000000..aab077ea558f8
--- /dev/null
+++ b/clang/test/Sema/builtin-stackaddress-target-support.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -verify %s -triple x86_64-unknown-unknown -DTEST_x64
+// RUN: %clang_cc1 -verify %s -triple i386-unknown-unknown -DTEST_x86
+// RUN: %clang_cc1 -verify %s -triple riscv32-unknown-unknown -DTEST_riscv32
+// RUN: %clang_cc1 -verify %s -triple riscv64-unknown-unknown -DTEST_riscv64
+// RUN: %clang_cc1 -verify %s -triple aarch64-unknown-unknown -DTEST_aarch64
+
+#if defined(TEST_x64) || defined(TEST_x86)
+// expected-no-diagnostics
+void *a() {
+return __builtin_stack_address();
+}
+#else
+void *a() {
+return __builtin_stack_address(); // expected-error {{builtin is not supported 
on this target}}
+}
+#endif
diff --git a/clang/test/Sema/builtin-stackaddress.c 
b/clang/test/Sema/builtin-stackaddress.c
index ecdc64d899af5..03a0f5ef16714 100644
--- a/clang/test/Sema/builtin-stackaddress.c
+++ b/clang/test/Sema/builtin-stackaddress.c
@@ -36,3 +36,8 @@ void* h(unsigned x) {
 // expected-error@+1 {{argument value 1048575 is outside the valid range [0, 
65535]}}
 return __builtin_frame_address(0xFFFFF);
 }
+
+void *i() {
+// expected-error@+1 {{too many arguments to function call, expected 0, have 
1}}
+return __builtin_stack_address(0);
+}
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h 
b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 465e4a0a9d0d8..916f277846a3f 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -121,6 +121,11 @@ enum NodeType {
   /// function calling this intrinsic.
   SPONENTRY,
 
+  /// STACKADDR - Represents the llvm.stackaddr intrinsic. Takes no argument
+  /// and returns the starting address of the stack region that may be used
+  /// by called functions.
+  STACKADDR,
+
   /// LOCAL_RECOVER - Represents the llvm.localrecover intrinsic.
   /// Materializes the offset from the local object pointer of another
   /// function to a particular local object passed to llvm.localescape. The
diff --git a/llvm/include/llvm/IR/Intrinsics.td 
b/llvm/include/llvm/IR/Intrinsics.td
index bd6f94ac1286c..42f73e67e5896 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -853,6 +853,7 @@ def int_addressofreturnaddress : 
DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [In
 def int_frameaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_i32_ty],
                                  [IntrNoMem, ImmArg<ArgIndex<0>>]>;
 def int_sponentry  : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [IntrNoMem]>;
+def int_stackaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], []>;
 def int_read_register  : DefaultAttrsIntrinsic<[llvm_anyint_ty], 
[llvm_metadata_ty],
                                    [IntrReadMem], "llvm.read_register">;
 def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty],
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp 
b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index 528136a55f14a..8ee85211da7bf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -1120,6 +1120,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
   case ISD::ADJUST_TRAMPOLINE:
   case ISD::FRAMEADDR:
   case ISD::RETURNADDR:
+  case ISD::STACKADDR:
   case ISD::ADDROFRETURNADDR:
   case ISD::SPONENTRY:
     // These operations lie about being legal: when they claim to be legal,
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp 
b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index ecd1ff87e7fbc..82548fb87abc5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -6522,6 +6522,12 @@ void SelectionDAGBuilder::visitIntrinsicCall(const 
CallInst &I,
                              TLI.getFrameIndexTy(DAG.getDataLayout()),
                              getValue(I.getArgOperand(0))));
     return;
+  case Intrinsic::stackaddress: {
+    setValue(&I,
+             DAG.getNode(ISD::STACKADDR, sdl,
+                         TLI.getValueType(DAG.getDataLayout(), I.getType())));
+    return;
+  }
   case Intrinsic::read_volatile_register:
   case Intrinsic::read_register: {
     Value *Reg = I.getArgOperand(0);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp 
b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 7fc15581c17e4..d29f50319694c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -148,6 +148,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) 
const {
   case ISD::ADDROFRETURNADDR:           return "ADDROFRETURNADDR";
   case ISD::FRAMEADDR:                  return "FRAMEADDR";
   case ISD::SPONENTRY:                  return "SPONENTRY";
+  case ISD::STACKADDR:                  return "STACKADDR";
   case ISD::LOCAL_RECOVER:              return "LOCAL_RECOVER";
   case ISD::READ_REGISTER:              return "READ_REGISTER";
   case ISD::WRITE_REGISTER:             return "WRITE_REGISTER";
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp 
b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 347ba1262b66b..7d3938154ecbc 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -28274,6 +28274,13 @@ SDValue X86TargetLowering::LowerFRAMEADDR(SDValue Op, 
SelectionDAG &DAG) const {
   return FrameAddr;
 }
 
+SDValue X86TargetLowering::LowerSTACKADDR(SDValue Op, SelectionDAG &DAG) const 
{
+  SDLoc dl(Op);
+  return DAG.getCopyFromReg(DAG.getEntryNode(), dl,
+                            Subtarget.getRegisterInfo()->getStackRegister(),
+                            Op->getValueType(0));
+}
+
 // FIXME? Maybe this could be a TableGen attribute on some registers and
 // this table could be generated automatically from RegInfo.
 Register X86TargetLowering::getRegisterByName(const char* RegName, LLT VT,
@@ -33637,6 +33644,7 @@ SDValue X86TargetLowering::LowerOperation(SDValue Op, 
SelectionDAG &DAG) const {
   case ISD::RETURNADDR:         return LowerRETURNADDR(Op, DAG);
   case ISD::ADDROFRETURNADDR:   return LowerADDROFRETURNADDR(Op, DAG);
   case ISD::FRAMEADDR:          return LowerFRAMEADDR(Op, DAG);
+  case ISD::STACKADDR:          return LowerSTACKADDR(Op, DAG);
   case ISD::FRAME_TO_ARGS_OFFSET:
                                 return LowerFRAME_TO_ARGS_OFFSET(Op, DAG);
   case ISD::DYNAMIC_STACKALLOC: return LowerDYNAMIC_STACKALLOC(Op, DAG);
diff --git a/llvm/lib/Target/X86/X86ISelLowering.h 
b/llvm/lib/Target/X86/X86ISelLowering.h
index 5cb6b3e493a32..f7856cc4f0fd7 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.h
+++ b/llvm/lib/Target/X86/X86ISelLowering.h
@@ -1771,6 +1771,7 @@ namespace llvm {
     SDValue LowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerADDROFRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
+    SDValue LowerSTACKADDR(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerFRAME_TO_ARGS_OFFSET(SDValue Op, SelectionDAG &DAG) const;
     SDValue LowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerEH_SJLJ_SETJMP(SDValue Op, SelectionDAG &DAG) const;

>From 0b5e0edc061239d8ea99f1d64034f542ea17aeaf Mon Sep 17 00:00:00 2001
From: moorabbit <moorab...@proton.me>
Date: Wed, 16 Jul 2025 16:07:59 -0400
Subject: [PATCH 2/2] [LLVM] Document 'llvm.stackaddress' Intrinsic

---
 llvm/docs/LangRef.rst | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d2a1821efd698..ca8b5f40a3f9f 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -14295,6 +14295,36 @@ Semantics:
 
 Note this intrinsic is only verified on AArch64 and ARM.
 
+'``llvm.stackaddress``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+      declare ptr @llvm.stackaddress()
+
+Overview:
+"""""""""
+
+The '``llvm.stackaddress``' instrinsic returns the starting address of the 
stack region that may be
+used by called functions.
+
+Semantics:
+""""""""""
+
+This intrinsic returns the *logical* value of the stack pointer register, that 
is, the address
+separating the stack space of the current function from the stack space that 
may be modified by
+called functions.
+
+On certain targets (e.g. x86), the logical and actual (or physical) values of 
the stack pointer
+register are the same. However, on other architectures (e.g. SPARCv9), the 
logical value of the
+stack pointer register may differ from the physical value. 
'``llvm.stackaddress``' handles this
+discrepancy and returns the correct boundary address.
+
+**Note**: This intrinsic is currently only implemented for x86 and x86-64.
+
 '``llvm.frameaddress``' Intrinsic
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to