[clang] [CIR] Implement __builtin_return_address and __builtin_frame_address (PR #153698)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 14 15:07:06 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Morris Hafner (mmha)
<details>
<summary>Changes</summary>
This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts.
---
Full diff: https://github.com/llvm/llvm-project/pull/153698.diff
8 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+61)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+3)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+14)
- (modified) clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h (+4-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+36)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+40)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h (+20)
- (modified) clang/test/CIR/CodeGen/builtins.cpp (+28)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a77e9199cdc96..20a75d52777c6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2210,6 +2210,67 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
];
}
+//===----------------------------------------------------------------------===//
+// ReturnAddrOp and FrameAddrOp
+//===----------------------------------------------------------------------===//
+
+class CIR_FuncAddrBuiltinOp<string mnemonic> : CIR_Op<mnemonic, []> {
+ let arguments = (ins CIR_UInt32:$level);
+ let results = (outs CIR_VoidPtrType:$result);
+ let assemblyFormat = [{
+ `(` $level `)` attr-dict
+ }];
+}
+
+def CIR_ReturnAddrOp : CIR_FuncAddrBuiltinOp<"return_address"> {
+ let summary =
+ "The return address of the current function, or of one of its callers";
+
+ let description = [{
+ Represents call to builtin function ` __builtin_return_address` in CIR.
+ This builtin function returns the return address of the current function,
+ or of one of its callers.
+ The `level` argument is number of frames to scan up the call stack.
+ For instance, value of 0 yields the return address of the current function,
+ value of 1 yields the return address of the caller of the current function,
+ and so forth.
+
+ Examples:
+
+ ```mlir
+ %p = return_address(%level) -> !cir.ptr<!void>
+ ```
+ }];
+}
+
+def CIR_FrameAddrOp : CIR_FuncAddrBuiltinOp<"frame_address"> {
+ let summary =
+ "The frame address of the current function, or of one of its callers";
+
+ let description = [{
+ Represents call to builtin function ` __builtin_frame_address` in CIR.
+ This builtin function returns the frame address of the current function,
+ or of one of its callers. The frame is the area on the stack that holds
+ local variables and saved registers. The frame address is normally the
+ address of the first word pushed on to the stack by the function.
+ However, the exact definition depends upon the processor and the calling
+ convention. If the processor has a dedicated frame pointer register, and
+ the function has a frame, then __builtin_frame_address returns the value of
+ the frame pointer register.
+
+ The `level` argument is number of frames to scan up the call stack.
+ For instance, value of 0 yields the frame address of the current function,
+ value of 1 yields the frame address of the caller of the current function,
+ and so forth.
+
+ Examples:
+
+ ```mlir
+ %p = frame_address(%level) -> !cir.ptr<!void>
+ ```
+ }];
+}
+
//===----------------------------------------------------------------------===//
// StackSaveOp & StackRestoreOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 8b2538c941f47..a3ff7c58f76ba 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -262,6 +262,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
return getConstantInt(loc, getSInt32Ty(), c);
}
+ cir::ConstantOp getUInt32(uint32_t c, mlir::Location loc) {
+ return getConstantInt(loc, getUInt32Ty(), c);
+ }
// Creates constant nullptr for pointer type ty.
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 36aea4c1d39ce..d5f930608cd39 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -312,6 +312,20 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_rotateright64:
return emitRotate(e, /*isRotateLeft=*/false);
+ case Builtin::BI__builtin_return_address:
+ case Builtin::BI__builtin_frame_address: {
+ mlir::Location loc = getLoc(e->getExprLoc());
+ mlir::Attribute levelAttr = ConstantEmitter(*this).emitAbstract(
+ e->getArg(0), e->getArg(0)->getType());
+ uint64_t level = mlir::cast<cir::IntAttr>(levelAttr).getUInt();
+ if (builtinID == Builtin::BI__builtin_return_address) {
+ return RValue::get(builder.create<cir::ReturnAddrOp>(
+ loc, builder.getUInt32(level, loc)));
+ }
+ return RValue::get(
+ builder.create<cir::FrameAddrOp>(loc, builder.getUInt32(level, loc)));
+ }
+
case Builtin::BI__builtin_trap:
emitTrap(loc, /*createNewBlock=*/true);
return RValue::get(nullptr);
diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
index d6dac50bb1263..d455f6e283406 100644
--- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -80,7 +80,7 @@ class ConstantEmitter {
// initializer or to propagate to another context; for example,
// side effects, or emitting an initialization that requires a
// reference to its current location.
- mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);
+ mlir::Attribute emitForMemory(mlir::Attribute c, QualType destType);
/// Try to emit the initializer of the given declaration as an abstract
/// constant.
@@ -90,8 +90,9 @@ class ConstantEmitter {
/// asserting that it succeeded. This is only safe to do when the
/// expression is known to be a constant expression with either a fairly
/// simple type or a known simple form.
+ mlir::Attribute emitAbstract(const Expr *e, QualType destType);
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
- QualType t);
+ QualType destType);
mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce);
@@ -101,6 +102,7 @@ class ConstantEmitter {
mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d);
+ mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType);
mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 87ea34df6be59..b0349e008d102 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -700,6 +700,16 @@ mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
return (c ? emitForMemory(c, destType) : nullptr);
}
+mlir::Attribute ConstantEmitter::emitAbstract(const Expr *e,
+ QualType destType) {
+ AbstractStateRAII state{*this, true};
+ mlir::Attribute c = mlir::cast<mlir::Attribute>(tryEmitPrivate(e, destType));
+ if (!c)
+ cgm.errorNYI(e->getSourceRange(),
+ "emitAbstract failed, emit null constaant");
+ return c;
+}
+
mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
const APValue &value,
QualType destType) {
@@ -721,6 +731,32 @@ mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
return c;
}
+mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e,
+ QualType destType) {
+ assert(!destType->isVoidType() && "can't emit a void constant");
+
+ if (mlir::Attribute c =
+ ConstExprEmitter(*this).Visit(const_cast<Expr *>(e), destType))
+ return llvm::dyn_cast<mlir::TypedAttr>(c);
+
+ Expr::EvalResult result;
+
+ bool success = false;
+
+ if (destType->isReferenceType())
+ success = e->EvaluateAsLValue(result, cgm.getASTContext());
+ else
+ success =
+ e->EvaluateAsRValue(result, cgm.getASTContext(), inConstantContext);
+
+ if (success && !result.hasSideEffects()) {
+ mlir::Attribute c = tryEmitPrivate(result.Val, destType);
+ return llvm::dyn_cast<mlir::TypedAttr>(c);
+ }
+
+ return nullptr;
+}
+
mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
QualType destType) {
auto &builder = cgm.getBuilder();
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 20b8787d4f55f..86714c07ff701 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -267,6 +267,26 @@ void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
}
}
+static mlir::LLVM::CallIntrinsicOp
+createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter,
+ mlir::Location loc, const llvm::Twine &intrinsicName,
+ mlir::Type resultTy, mlir::ValueRange operands) {
+ auto intrinsicNameAttr =
+ mlir::StringAttr::get(rewriter.getContext(), intrinsicName);
+ return rewriter.create<mlir::LLVM::CallIntrinsicOp>(
+ loc, resultTy, intrinsicNameAttr, operands);
+}
+
+static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
+ mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op,
+ const llvm::Twine &intrinsicName, mlir::Type resultTy,
+ mlir::ValueRange operands) {
+ mlir::LLVM::CallIntrinsicOp callIntrinOp = createCallLLVMIntrinsicOp(
+ rewriter, op->getLoc(), intrinsicName, resultTy, operands);
+ rewriter.replaceOp(op, callIntrinOp.getOperation());
+ return callIntrinOp;
+}
+
/// IntAttr visitor.
mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
mlir::Location loc = parentOp->getLoc();
@@ -1097,6 +1117,24 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
getTypeConverter(), op.getCalleeAttr());
}
+mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
+ cir::ReturnAddrOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress",
+ llvmPtrTy, adaptor.getOperands());
+ return mlir::success();
+}
+
+mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
+ cir::FrameAddrOp op, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const {
+ auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+ replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.frameaddress", llvmPtrTy,
+ adaptor.getOperands());
+ return mlir::success();
+}
+
mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
cir::LoadOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2307,10 +2345,12 @@ void ConvertCIRToLLVMPass::runOnOperation() {
CIRToLLVMConstantOpLowering,
CIRToLLVMExpectOpLowering,
CIRToLLVMFAbsOpLowering,
+ CIRToLLVMFrameAddrOpLowering,
CIRToLLVMFuncOpLowering,
CIRToLLVMGetBitfieldOpLowering,
CIRToLLVMGetGlobalOpLowering,
CIRToLLVMGetMemberOpLowering,
+ CIRToLLVMReturnAddrOpLowering,
CIRToLLVMRotateOpLowering,
CIRToLLVMSelectOpLowering,
CIRToLLVMSetBitfieldOpLowering,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index e32bf2d1bae0c..740e10897338f 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -209,6 +209,26 @@ class CIRToLLVMCallOpLowering : public mlir::OpConversionPattern<cir::CallOp> {
mlir::ConversionPatternRewriter &rewriter) const override;
};
+class CIRToLLVMReturnAddrOpLowering
+ : public mlir::OpConversionPattern<cir::ReturnAddrOp> {
+public:
+ using mlir::OpConversionPattern<cir::ReturnAddrOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::ReturnAddrOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
+class CIRToLLVMFrameAddrOpLowering
+ : public mlir::OpConversionPattern<cir::FrameAddrOp> {
+public:
+ using mlir::OpConversionPattern<cir::FrameAddrOp>::OpConversionPattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(cir::FrameAddrOp op, OpAdaptor,
+ mlir::ConversionPatternRewriter &) const override;
+};
+
class CIRToLLVMAllocaOpLowering
: public mlir::OpConversionPattern<cir::AllocaOp> {
mlir::DataLayout const &dataLayout;
diff --git a/clang/test/CIR/CodeGen/builtins.cpp b/clang/test/CIR/CodeGen/builtins.cpp
index 3d43821af4e51..0e434809fe6be 100644
--- a/clang/test/CIR/CodeGen/builtins.cpp
+++ b/clang/test/CIR/CodeGen/builtins.cpp
@@ -12,3 +12,31 @@ double fabs(double x) {
// CIR: {{.*}} = cir.fabs {{.*}} : !cir.double
// LLVM: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
// OGCG: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
+
+extern "C" void *test_return_address(void) {
+ return __builtin_return_address(1);
+
+ // CIR-LABEL: test_return_address
+ // CIR: [[ARG:%.*]] = cir.const #cir.int<1> : !u32i
+ // CIR: {{%.*}} = cir.return_address([[ARG]])
+
+ // LLVM-LABEL: @test_return_address
+ // LLVM: {{%.*}} = call ptr @llvm.returnaddress(i32 1)
+
+ // OGCG-LABEL: @test_return_address
+ // OGCG: {{%.*}} = call ptr @llvm.returnaddress(i32 1)
+}
+
+extern "C" void *test_frame_address(void) {
+ return __builtin_frame_address(1);
+
+ // CIR-LABEL: test_frame_address
+ // CIR: [[ARG:%.*]] = cir.const #cir.int<1> : !u32i
+ // CIR: {{%.*}} = cir.frame_address([[ARG]])
+
+ // LLVM-LABEL: @test_frame_address
+ // LLVM: {{%.*}} = call ptr @llvm.frameaddress.p0(i32 1)
+
+ // OGCG-LABEL: @test_frame_address
+ // OGCG: {{%.*}} = call ptr @llvm.frameaddress.p0(i32 1)
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/153698
More information about the cfe-commits
mailing list