[clang] [Clang] Add GCC's __builtin_stack_address() to Clang. (PR #121332)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 3 01:31:02 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] [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();
+}
More information about the cfe-commits
mailing list