-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Implement __builtin_return_address and __builtin_frame_address #153698
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts.
@llvm/pr-subscribers-clangir @llvm/pr-subscribers-clang Author: Morris Hafner (mmha) ChangesThis 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:
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)
+}
|
cir::ReturnAddrOp op, OpAdaptor adaptor, | ||
mlir::ConversionPatternRewriter &rewriter) const { | ||
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); | ||
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we're lowering these to an LLVM intrinsic call anyway, I wonder if we should just use cir.llvm.intrinsic
rather than create dedicated ops for them.
@bcardosolopes Were these useful for some kind of analysis?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The information coming from these builtins are likely useful in order to mark some function as unsafe for certain optimizations (e.g. where the return address or stack addressed in general might escape), they feel very different IMO than random shift vectors for ARM SVE. That said, you are right we don't use them right now, but I'd argue for keeping them because their behavior could be more intrusive than regular intrinsics.
Co-authored-by: Andy Kaylor <[email protected]>
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
cir::ReturnAddrOp op, OpAdaptor adaptor, | ||
mlir::ConversionPatternRewriter &rewriter) const { | ||
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); | ||
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The information coming from these builtins are likely useful in order to mark some function as unsafe for certain optimizations (e.g. where the return address or stack addressed in general might escape), they feel very different IMO than random shift vectors for ARM SVE. That said, you are right we don't use them right now, but I'd argue for keeping them because their behavior could be more intrusive than regular intrinsics.
This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts.