[clang] [Clang] Add GCC's __builtin_stack_address() to Clang. (PR #121332)

via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 5 05:58:18 PST 2025


https://github.com/aalhwc updated https://github.com/llvm/llvm-project/pull/121332

>From f247c1ab9fa89ca09476ed0a398a4c4385e75193 Mon Sep 17 00:00:00 2001
From: aalhwc <aalhwc at gmail.com>
Date: Mon, 30 Dec 2024 05:03:59 -0500
Subject: [PATCH 1/3] [Clang] Add GCC's __builtin_stack_address() to Clang
 (#82632).

This new builtin returns the value of the stack pointer register,
mirroring GCC's __builtin_stack_address(). This implementation initially
supports only the x86 and x86_64 architectures. Support for other
architectures can be added in future patches.
---
 clang/include/clang/Basic/Builtins.td     |   6 +
 clang/lib/CodeGen/CGBuiltin.cpp           | 163 ++++++++++++----------
 clang/test/CodeGen/builtin-stackaddress.c |  13 ++
 3 files changed, 107 insertions(+), 75 deletions(-)
 create mode 100644 clang/test/CodeGen/builtin-stackaddress.c

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index b5b47ae2746011..69aed2e6b2f0ca 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -899,6 +899,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 4d4b7428abd505..263d77d8e10ecd 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2764,6 +2764,81 @@ static RValue EmitHipStdParUnsupportedBuiltin(CodeGenFunction *CGF,
   return RValue::get(CGF->Builder.CreateCall(UBF, Args));
 }
 
+enum SpecialRegisterAccessKind {
+  NormalRead,
+  VolatileRead,
+  Write,
+};
+
+// Generates the IR for the read/write special register builtin,
+// ValueType is the type of the value that is to be written or read,
+// RegisterType is the type of the register being written to or read from.
+static Value *EmitSpecialRegisterBuiltin(CodeGenFunction &CGF,
+                                         const CallExpr *E,
+                                         llvm::Type *RegisterType,
+                                         llvm::Type *ValueType,
+                                         SpecialRegisterAccessKind AccessKind,
+                                         StringRef SysReg = "") {
+  // write and register intrinsics only support 32, 64 and 128 bit operations.
+  assert((RegisterType->isIntegerTy(32) || RegisterType->isIntegerTy(64) ||
+          RegisterType->isIntegerTy(128)) &&
+         "Unsupported size for register.");
+
+  CodeGen::CGBuilderTy &Builder = CGF.Builder;
+  CodeGen::CodeGenModule &CGM = CGF.CGM;
+  LLVMContext &Context = CGM.getLLVMContext();
+
+  if (SysReg.empty()) {
+    const Expr *SysRegStrExpr = E->getArg(0)->IgnoreParenCasts();
+    SysReg = cast<clang::StringLiteral>(SysRegStrExpr)->getString();
+  }
+
+  llvm::Metadata *Ops[] = {llvm::MDString::get(Context, SysReg)};
+  llvm::MDNode *RegName = llvm::MDNode::get(Context, Ops);
+  llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
+
+  llvm::Type *Types[] = {RegisterType};
+
+  bool MixedTypes = RegisterType->isIntegerTy(64) && ValueType->isIntegerTy(32);
+  assert(!(RegisterType->isIntegerTy(32) && ValueType->isIntegerTy(64)) &&
+         "Can't fit 64-bit value in 32-bit register");
+
+  if (AccessKind != Write) {
+    assert(AccessKind == NormalRead || AccessKind == VolatileRead);
+    llvm::Function *F = CGM.getIntrinsic(
+        AccessKind == VolatileRead ? llvm::Intrinsic::read_volatile_register
+                                   : llvm::Intrinsic::read_register,
+        Types);
+    llvm::Value *Call = Builder.CreateCall(F, Metadata);
+
+    if (MixedTypes)
+      // Read into 64 bit register and then truncate result to 32 bit.
+      return Builder.CreateTrunc(Call, ValueType);
+
+    if (ValueType->isPointerTy())
+      // Have i32/i64 result (Call) but want to return a VoidPtrTy (i8*).
+      return Builder.CreateIntToPtr(Call, ValueType);
+
+    return Call;
+  }
+
+  llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::write_register, Types);
+  llvm::Value *ArgValue = CGF.EmitScalarExpr(E->getArg(1));
+  if (MixedTypes) {
+    // Extend 32 bit write value to 64 bit to pass to write.
+    ArgValue = Builder.CreateZExt(ArgValue, RegisterType);
+    return Builder.CreateCall(F, {Metadata, ArgValue});
+  }
+
+  if (ValueType->isPointerTy()) {
+    // Have VoidPtrTy ArgValue but want to return an i32/i64.
+    ArgValue = Builder.CreatePtrToInt(ArgValue, RegisterType);
+    return Builder.CreateCall(F, {Metadata, ArgValue});
+  }
+
+  return Builder.CreateCall(F, {Metadata, ArgValue});
+}
+
 RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
                                         const CallExpr *E,
                                         ReturnValueSlot ReturnValue) {
@@ -4782,6 +4857,19 @@ 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: {
+    switch (getTarget().getTriple().getArch()) {
+    case Triple::x86:
+      return RValue::get(EmitSpecialRegisterBuiltin(
+          *this, E, Int32Ty, VoidPtrTy, NormalRead, "esp"));
+    case Triple::x86_64:
+      return RValue::get(EmitSpecialRegisterBuiltin(
+          *this, E, Int64Ty, VoidPtrTy, NormalRead, "rsp"));
+    default:
+      ErrorUnsupported(E, "__builtin_stack_address");
+      return GetUndefRValue(E->getType());
+    }
+  }
   case Builtin::BI__builtin_extract_return_addr: {
     Value *Address = EmitScalarExpr(E->getArg(0));
     Value *Result = getTargetHooks().decodeReturnAddress(*this, Address);
@@ -8897,12 +8985,6 @@ Value *CodeGenFunction::GetValueForARMHint(unsigned BuiltinID) {
                             llvm::ConstantInt::get(Int32Ty, Value));
 }
 
-enum SpecialRegisterAccessKind {
-  NormalRead,
-  VolatileRead,
-  Write,
-};
-
 // Generates the IR for __builtin_read_exec_*.
 // Lowers the builtin to amdgcn_ballot intrinsic.
 static Value *EmitAMDGCNBallotForExec(CodeGenFunction &CGF, const CallExpr *E,
@@ -8923,75 +9005,6 @@ static Value *EmitAMDGCNBallotForExec(CodeGenFunction &CGF, const CallExpr *E,
   return Call;
 }
 
-// Generates the IR for the read/write special register builtin,
-// ValueType is the type of the value that is to be written or read,
-// RegisterType is the type of the register being written to or read from.
-static Value *EmitSpecialRegisterBuiltin(CodeGenFunction &CGF,
-                                         const CallExpr *E,
-                                         llvm::Type *RegisterType,
-                                         llvm::Type *ValueType,
-                                         SpecialRegisterAccessKind AccessKind,
-                                         StringRef SysReg = "") {
-  // write and register intrinsics only support 32, 64 and 128 bit operations.
-  assert((RegisterType->isIntegerTy(32) || RegisterType->isIntegerTy(64) ||
-          RegisterType->isIntegerTy(128)) &&
-         "Unsupported size for register.");
-
-  CodeGen::CGBuilderTy &Builder = CGF.Builder;
-  CodeGen::CodeGenModule &CGM = CGF.CGM;
-  LLVMContext &Context = CGM.getLLVMContext();
-
-  if (SysReg.empty()) {
-    const Expr *SysRegStrExpr = E->getArg(0)->IgnoreParenCasts();
-    SysReg = cast<clang::StringLiteral>(SysRegStrExpr)->getString();
-  }
-
-  llvm::Metadata *Ops[] = { llvm::MDString::get(Context, SysReg) };
-  llvm::MDNode *RegName = llvm::MDNode::get(Context, Ops);
-  llvm::Value *Metadata = llvm::MetadataAsValue::get(Context, RegName);
-
-  llvm::Type *Types[] = { RegisterType };
-
-  bool MixedTypes = RegisterType->isIntegerTy(64) && ValueType->isIntegerTy(32);
-  assert(!(RegisterType->isIntegerTy(32) && ValueType->isIntegerTy(64))
-            && "Can't fit 64-bit value in 32-bit register");
-
-  if (AccessKind != Write) {
-    assert(AccessKind == NormalRead || AccessKind == VolatileRead);
-    llvm::Function *F = CGM.getIntrinsic(
-        AccessKind == VolatileRead ? llvm::Intrinsic::read_volatile_register
-                                   : llvm::Intrinsic::read_register,
-        Types);
-    llvm::Value *Call = Builder.CreateCall(F, Metadata);
-
-    if (MixedTypes)
-      // Read into 64 bit register and then truncate result to 32 bit.
-      return Builder.CreateTrunc(Call, ValueType);
-
-    if (ValueType->isPointerTy())
-      // Have i32/i64 result (Call) but want to return a VoidPtrTy (i8*).
-      return Builder.CreateIntToPtr(Call, ValueType);
-
-    return Call;
-  }
-
-  llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::write_register, Types);
-  llvm::Value *ArgValue = CGF.EmitScalarExpr(E->getArg(1));
-  if (MixedTypes) {
-    // Extend 32 bit write value to 64 bit to pass to write.
-    ArgValue = Builder.CreateZExt(ArgValue, RegisterType);
-    return Builder.CreateCall(F, { Metadata, ArgValue });
-  }
-
-  if (ValueType->isPointerTy()) {
-    // Have VoidPtrTy ArgValue but want to return an i32/i64.
-    ArgValue = Builder.CreatePtrToInt(ArgValue, RegisterType);
-    return Builder.CreateCall(F, { Metadata, ArgValue });
-  }
-
-  return Builder.CreateCall(F, { Metadata, ArgValue });
-}
-
 /// Return true if BuiltinID is an overloaded Neon intrinsic with an extra
 /// argument that specifies the vector type.
 static bool HasExtraNeonArgument(unsigned BuiltinID) {
diff --git a/clang/test/CodeGen/builtin-stackaddress.c b/clang/test/CodeGen/builtin-stackaddress.c
new file mode 100644
index 00000000000000..e7f37a56e3acea
--- /dev/null
+++ b/clang/test/CodeGen/builtin-stackaddress.c
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple i686-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86_64 %s
+
+void* a() {
+  // X86_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
+  // X86_64: inttoptr i64 [[INT_SP]]
+  // X86_64: [[SPREG]] = !{!"rsp"}
+  //
+  // X86: [[INT_SP:%.*]] = call i32 @llvm.read_register.i32(metadata [[SPREG:![0-9]+]])
+  // X86: inttoptr i32 [[INT_SP]]
+  // X86: [[SPREG]] = !{!"esp"}
+  return __builtin_stack_address();
+}

>From b1dd634a8d1f8cb9e3c6c3a68f2446194a247960 Mon Sep 17 00:00:00 2001
From: aalhwc <aalhwc at gmail.com>
Date: Sat, 4 Jan 2025 05:34:46 -0500
Subject: [PATCH 2/3] [Clang] Add RISCV support for `__builtin_stack_address`.

Read the 'sp' register, replicating GCC's implementation of
`__builtin_stack_address` on RISCV: https://godbolt.org/z/fGraxYoxd
---
 clang/lib/CodeGen/CGBuiltin.cpp           |  8 ++++++++
 clang/test/CodeGen/builtin-stackaddress.c | 10 ++++++++++
 2 files changed, 18 insertions(+)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 263d77d8e10ecd..53a047e6553fd9 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4865,6 +4865,14 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Triple::x86_64:
       return RValue::get(EmitSpecialRegisterBuiltin(
           *this, E, Int64Ty, VoidPtrTy, NormalRead, "rsp"));
+    case Triple::riscv32:
+    case Triple::riscv64: {
+      llvm::IntegerType *SPRegIntTy =
+          getTarget().getTriple().getArchPointerBitWidth() == 64 ? Int64Ty
+                                                                 : Int32Ty;
+      return RValue::get(EmitSpecialRegisterBuiltin(
+          *this, E, SPRegIntTy, VoidPtrTy, NormalRead, "sp"));
+    }
     default:
       ErrorUnsupported(E, "__builtin_stack_address");
       return GetUndefRValue(E->getType());
diff --git a/clang/test/CodeGen/builtin-stackaddress.c b/clang/test/CodeGen/builtin-stackaddress.c
index e7f37a56e3acea..40247294daa5b9 100644
--- a/clang/test/CodeGen/builtin-stackaddress.c
+++ b/clang/test/CodeGen/builtin-stackaddress.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -triple i686-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86 %s
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86_64 %s
+// RUN: %clang_cc1 -triple riscv32-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_32 %s
+// RUN: %clang_cc1 -triple riscv64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_64 %s
 
 void* a() {
   // X86_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
@@ -9,5 +11,13 @@ void* a() {
   // X86: [[INT_SP:%.*]] = call i32 @llvm.read_register.i32(metadata [[SPREG:![0-9]+]])
   // X86: inttoptr i32 [[INT_SP]]
   // X86: [[SPREG]] = !{!"esp"}
+  //
+  // RISCV_32: [[INT_SP:%.*]] = call i32 @llvm.read_register.i32(metadata [[SPREG:![0-9]+]])
+  // RISCV_32: inttoptr i32 [[INT_SP]]
+  // RISCV_32: [[SPREG]] = !{!"sp"}
+  //
+  // RISCV_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
+  // RISCV_64: inttoptr i64 [[INT_SP]]
+  // RISCV_64: [[SPREG]] = !{!"sp"}
   return __builtin_stack_address();
 }

>From 075fa8a134b306f7d3f8b061865c0002eaf8ef51 Mon Sep 17 00:00:00 2001
From: aalhwc <aalhwc at gmail.com>
Date: Sun, 5 Jan 2025 08:56:04 -0500
Subject: [PATCH 3/3] [Clang] Add ARM support for `__builtin_stack_address`.

Read the 'sp' register, replicating GCC's implementation of
`__builtin_stack_address` on ARM: https://godbolt.org/z/4EY531j89
---
 clang/lib/CodeGen/CGBuiltin.cpp           |  7 +++++++
 clang/test/CodeGen/builtin-stackaddress.c | 18 ++++++++++--------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 53a047e6553fd9..185eeb5ec8a68b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4865,6 +4865,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     case Triple::x86_64:
       return RValue::get(EmitSpecialRegisterBuiltin(
           *this, E, Int64Ty, VoidPtrTy, NormalRead, "rsp"));
+    case Triple::arm:
+    case Triple::armeb:
+    case Triple::thumb:
+    case Triple::thumbeb:
+    case Triple::aarch64:
+    case Triple::aarch64_be:
+    case Triple::aarch64_32:
     case Triple::riscv32:
     case Triple::riscv64: {
       llvm::IntegerType *SPRegIntTy =
diff --git a/clang/test/CodeGen/builtin-stackaddress.c b/clang/test/CodeGen/builtin-stackaddress.c
index 40247294daa5b9..210e4eba93b428 100644
--- a/clang/test/CodeGen/builtin-stackaddress.c
+++ b/clang/test/CodeGen/builtin-stackaddress.c
@@ -1,7 +1,9 @@
 // RUN: %clang_cc1 -triple i686-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86 %s
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=X86_64 %s
-// RUN: %clang_cc1 -triple riscv32-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_32 %s
-// RUN: %clang_cc1 -triple riscv64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_64 %s
+// RUN: %clang_cc1 -triple riscv32-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_ARM_32 %s
+// RUN: %clang_cc1 -triple riscv64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_ARM_64 %s
+// RUN: %clang_cc1 -triple arm-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_ARM_32 %s
+// RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm -o - %s | FileCheck -check-prefix=RISCV_ARM_64 %s
 
 void* a() {
   // X86_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
@@ -12,12 +14,12 @@ void* a() {
   // X86: inttoptr i32 [[INT_SP]]
   // X86: [[SPREG]] = !{!"esp"}
   //
-  // RISCV_32: [[INT_SP:%.*]] = call i32 @llvm.read_register.i32(metadata [[SPREG:![0-9]+]])
-  // RISCV_32: inttoptr i32 [[INT_SP]]
-  // RISCV_32: [[SPREG]] = !{!"sp"}
+  // RISCV_ARM_32: [[INT_SP:%.*]] = call i32 @llvm.read_register.i32(metadata [[SPREG:![0-9]+]])
+  // RISCV_ARM_32: inttoptr i32 [[INT_SP]]
+  // RISCV_ARM_32: [[SPREG]] = !{!"sp"}
   //
-  // RISCV_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
-  // RISCV_64: inttoptr i64 [[INT_SP]]
-  // RISCV_64: [[SPREG]] = !{!"sp"}
+  // RISCV_ARM_64: [[INT_SP:%.*]] = call i64 @llvm.read_register.i64(metadata [[SPREG:![0-9]+]])
+  // RISCV_ARM_64: inttoptr i64 [[INT_SP]]
+  // RISCV_ARM_64: [[SPREG]] = !{!"sp"}
   return __builtin_stack_address();
 }



More information about the cfe-commits mailing list