https://github.com/mmha created https://github.com/llvm/llvm-project/pull/153698
This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts. >From 1de984292efcc907405812ccb02f348e0a36e0af Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Fri, 15 Aug 2025 00:05:14 +0200 Subject: [PATCH] [CIR] Implement __builtin_return_address and __builtin_frame_address This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts. --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 61 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenBuilder.h | 3 + clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 14 +++++ clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h | 6 +- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 36 +++++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 40 ++++++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 20 ++++++ clang/test/CIR/CodeGen/builtins.cpp | 28 +++++++++ 8 files changed, 206 insertions(+), 2 deletions(-) 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) +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits