[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