Skip to content

Commit df0e9f3

Browse files
mmhaandykaylor
andauthored
[CIR] Implement __builtin_return_address and __builtin_frame_address (#153698)
This adds ReturnAddrOp and FrameAddrOp that represent __builtin_return_address and __builtin_frame_address and the respective lowering to LLVM parts. --------- Co-authored-by: Andy Kaylor <[email protected]>
1 parent f1fc507 commit df0e9f3

File tree

8 files changed

+207
-2
lines changed

8 files changed

+207
-2
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2243,6 +2243,68 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
22432243
];
22442244
}
22452245

2246+
//===----------------------------------------------------------------------===//
2247+
// ReturnAddrOp and FrameAddrOp
2248+
//===----------------------------------------------------------------------===//
2249+
2250+
class CIR_FuncAddrBuiltinOp<string mnemonic> : CIR_Op<mnemonic, []> {
2251+
let arguments = (ins CIR_UInt32:$level);
2252+
let results = (outs CIR_VoidPtrType:$result);
2253+
let assemblyFormat = [{
2254+
`(` $level `)` attr-dict
2255+
}];
2256+
}
2257+
2258+
def CIR_ReturnAddrOp : CIR_FuncAddrBuiltinOp<"return_address"> {
2259+
let summary =
2260+
"The return address of the current function, or of one of its callers";
2261+
2262+
let description = [{
2263+
Represents a call to builtin function ` __builtin_return_address` in CIR.
2264+
This builtin function returns the return address of the current function,
2265+
or of one of its callers.
2266+
2267+
The `level` argument is number of frames to scan up the call stack.
2268+
For instance, value of 0 yields the return address of the current function,
2269+
value of 1 yields the return address of the caller of the current function,
2270+
and so forth.
2271+
2272+
Examples:
2273+
2274+
```mlir
2275+
%p = return_address(%level) -> !cir.ptr<!void>
2276+
```
2277+
}];
2278+
}
2279+
2280+
def CIR_FrameAddrOp : CIR_FuncAddrBuiltinOp<"frame_address"> {
2281+
let summary =
2282+
"The frame address of the current function, or of one of its callers";
2283+
2284+
let description = [{
2285+
Represents a call to builtin function ` __builtin_frame_address` in CIR.
2286+
This builtin function returns the frame address of the current function,
2287+
or of one of its callers. The frame is the area on the stack that holds
2288+
local variables and saved registers. The frame address is normally the
2289+
address of the first word pushed on to the stack by the function.
2290+
However, the exact definition depends upon the processor and the calling
2291+
convention. If the processor has a dedicated frame pointer register, and
2292+
the function has a frame, then __builtin_frame_address returns the value of
2293+
the frame pointer register.
2294+
2295+
The `level` argument is number of frames to scan up the call stack.
2296+
For instance, value of 0 yields the frame address of the current function,
2297+
value of 1 yields the frame address of the caller of the current function,
2298+
and so forth.
2299+
2300+
Examples:
2301+
2302+
```mlir
2303+
%p = frame_address(%level) -> !cir.ptr<!void>
2304+
```
2305+
}];
2306+
}
2307+
22462308
//===----------------------------------------------------------------------===//
22472309
// StackSaveOp & StackRestoreOp
22482310
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
267267
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
268268
return getConstantInt(loc, getSInt32Ty(), c);
269269
}
270+
cir::ConstantOp getUInt32(uint32_t c, mlir::Location loc) {
271+
return getConstantInt(loc, getUInt32Ty(), c);
272+
}
270273

271274
// Creates constant nullptr for pointer type ty.
272275
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,20 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
312312
case Builtin::BI__builtin_rotateright64:
313313
return emitRotate(e, /*isRotateLeft=*/false);
314314

315+
case Builtin::BI__builtin_return_address:
316+
case Builtin::BI__builtin_frame_address: {
317+
mlir::Location loc = getLoc(e->getExprLoc());
318+
llvm::APSInt level = e->getArg(0)->EvaluateKnownConstInt(getContext());
319+
if (builtinID == Builtin::BI__builtin_return_address) {
320+
return RValue::get(cir::ReturnAddrOp::create(
321+
builder, loc,
322+
builder.getConstAPInt(loc, builder.getUInt32Ty(), level)));
323+
}
324+
return RValue::get(cir::FrameAddrOp::create(
325+
builder, loc,
326+
builder.getConstAPInt(loc, builder.getUInt32Ty(), level)));
327+
}
328+
315329
case Builtin::BI__builtin_trap:
316330
emitTrap(loc, /*createNewBlock=*/true);
317331
return RValue::get(nullptr);

clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class ConstantEmitter {
8080
// initializer or to propagate to another context; for example,
8181
// side effects, or emitting an initialization that requires a
8282
// reference to its current location.
83-
mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);
83+
mlir::Attribute emitForMemory(mlir::Attribute c, QualType destType);
8484

8585
/// Try to emit the initializer of the given declaration as an abstract
8686
/// constant.
@@ -90,8 +90,9 @@ class ConstantEmitter {
9090
/// asserting that it succeeded. This is only safe to do when the
9191
/// expression is known to be a constant expression with either a fairly
9292
/// simple type or a known simple form.
93+
mlir::Attribute emitAbstract(const Expr *e, QualType destType);
9394
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
94-
QualType t);
95+
QualType destType);
9596

9697
mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce);
9798

@@ -101,6 +102,7 @@ class ConstantEmitter {
101102

102103
mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d);
103104

105+
mlir::TypedAttr tryEmitPrivate(const Expr *e, QualType destType);
104106
mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
105107
mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);
106108

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,16 @@ mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
710710
return (c ? emitForMemory(c, destType) : nullptr);
711711
}
712712

713+
mlir::Attribute ConstantEmitter::emitAbstract(const Expr *e,
714+
QualType destType) {
715+
AbstractStateRAII state{*this, true};
716+
mlir::Attribute c = mlir::cast<mlir::Attribute>(tryEmitPrivate(e, destType));
717+
if (!c)
718+
cgm.errorNYI(e->getSourceRange(),
719+
"emitAbstract failed, emit null constaant");
720+
return c;
721+
}
722+
713723
mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
714724
const APValue &value,
715725
QualType destType) {
@@ -731,6 +741,32 @@ mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
731741
return c;
732742
}
733743

744+
mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e,
745+
QualType destType) {
746+
assert(!destType->isVoidType() && "can't emit a void constant");
747+
748+
if (mlir::Attribute c =
749+
ConstExprEmitter(*this).Visit(const_cast<Expr *>(e), destType))
750+
return llvm::dyn_cast<mlir::TypedAttr>(c);
751+
752+
Expr::EvalResult result;
753+
754+
bool success = false;
755+
756+
if (destType->isReferenceType())
757+
success = e->EvaluateAsLValue(result, cgm.getASTContext());
758+
else
759+
success =
760+
e->EvaluateAsRValue(result, cgm.getASTContext(), inConstantContext);
761+
762+
if (success && !result.hasSideEffects()) {
763+
mlir::Attribute c = tryEmitPrivate(result.Val, destType);
764+
return llvm::dyn_cast<mlir::TypedAttr>(c);
765+
}
766+
767+
return nullptr;
768+
}
769+
734770
mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
735771
QualType destType) {
736772
auto &builder = cgm.getBuilder();

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,26 @@ void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
267267
}
268268
}
269269

270+
static mlir::LLVM::CallIntrinsicOp
271+
createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter,
272+
mlir::Location loc, const llvm::Twine &intrinsicName,
273+
mlir::Type resultTy, mlir::ValueRange operands) {
274+
auto intrinsicNameAttr =
275+
mlir::StringAttr::get(rewriter.getContext(), intrinsicName);
276+
return mlir::LLVM::CallIntrinsicOp::create(rewriter, loc, resultTy,
277+
intrinsicNameAttr, operands);
278+
}
279+
280+
static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
281+
mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op,
282+
const llvm::Twine &intrinsicName, mlir::Type resultTy,
283+
mlir::ValueRange operands) {
284+
mlir::LLVM::CallIntrinsicOp callIntrinOp = createCallLLVMIntrinsicOp(
285+
rewriter, op->getLoc(), intrinsicName, resultTy, operands);
286+
rewriter.replaceOp(op, callIntrinOp.getOperation());
287+
return callIntrinOp;
288+
}
289+
270290
/// IntAttr visitor.
271291
mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
272292
mlir::Location loc = parentOp->getLoc();
@@ -1112,6 +1132,24 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
11121132
getTypeConverter(), op.getCalleeAttr());
11131133
}
11141134

1135+
mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
1136+
cir::ReturnAddrOp op, OpAdaptor adaptor,
1137+
mlir::ConversionPatternRewriter &rewriter) const {
1138+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
1139+
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress",
1140+
llvmPtrTy, adaptor.getOperands());
1141+
return mlir::success();
1142+
}
1143+
1144+
mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
1145+
cir::FrameAddrOp op, OpAdaptor adaptor,
1146+
mlir::ConversionPatternRewriter &rewriter) const {
1147+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
1148+
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.frameaddress", llvmPtrTy,
1149+
adaptor.getOperands());
1150+
return mlir::success();
1151+
}
1152+
11151153
mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
11161154
cir::LoadOp op, OpAdaptor adaptor,
11171155
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2322,10 +2360,12 @@ void ConvertCIRToLLVMPass::runOnOperation() {
23222360
CIRToLLVMConstantOpLowering,
23232361
CIRToLLVMExpectOpLowering,
23242362
CIRToLLVMFAbsOpLowering,
2363+
CIRToLLVMFrameAddrOpLowering,
23252364
CIRToLLVMFuncOpLowering,
23262365
CIRToLLVMGetBitfieldOpLowering,
23272366
CIRToLLVMGetGlobalOpLowering,
23282367
CIRToLLVMGetMemberOpLowering,
2368+
CIRToLLVMReturnAddrOpLowering,
23292369
CIRToLLVMRotateOpLowering,
23302370
CIRToLLVMSelectOpLowering,
23312371
CIRToLLVMSetBitfieldOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,26 @@ class CIRToLLVMCallOpLowering : public mlir::OpConversionPattern<cir::CallOp> {
209209
mlir::ConversionPatternRewriter &rewriter) const override;
210210
};
211211

212+
class CIRToLLVMReturnAddrOpLowering
213+
: public mlir::OpConversionPattern<cir::ReturnAddrOp> {
214+
public:
215+
using mlir::OpConversionPattern<cir::ReturnAddrOp>::OpConversionPattern;
216+
217+
mlir::LogicalResult
218+
matchAndRewrite(cir::ReturnAddrOp op, OpAdaptor,
219+
mlir::ConversionPatternRewriter &) const override;
220+
};
221+
222+
class CIRToLLVMFrameAddrOpLowering
223+
: public mlir::OpConversionPattern<cir::FrameAddrOp> {
224+
public:
225+
using mlir::OpConversionPattern<cir::FrameAddrOp>::OpConversionPattern;
226+
227+
mlir::LogicalResult
228+
matchAndRewrite(cir::FrameAddrOp op, OpAdaptor,
229+
mlir::ConversionPatternRewriter &) const override;
230+
};
231+
212232
class CIRToLLVMAllocaOpLowering
213233
: public mlir::OpConversionPattern<cir::AllocaOp> {
214234
mlir::DataLayout const &dataLayout;

clang/test/CIR/CodeGen/builtins.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,31 @@ double fabs(double x) {
1212
// CIR: {{.*}} = cir.fabs {{.*}} : !cir.double
1313
// LLVM: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
1414
// OGCG: {{.*}} = call double @llvm.fabs.f64(double {{.*}})
15+
16+
extern "C" void *test_return_address(void) {
17+
return __builtin_return_address(1);
18+
19+
// CIR-LABEL: test_return_address
20+
// CIR: [[ARG:%.*]] = cir.const #cir.int<1> : !u32i
21+
// CIR: {{%.*}} = cir.return_address([[ARG]])
22+
23+
// LLVM-LABEL: @test_return_address
24+
// LLVM: {{%.*}} = call ptr @llvm.returnaddress(i32 1)
25+
26+
// OGCG-LABEL: @test_return_address
27+
// OGCG: {{%.*}} = call ptr @llvm.returnaddress(i32 1)
28+
}
29+
30+
extern "C" void *test_frame_address(void) {
31+
return __builtin_frame_address(1);
32+
33+
// CIR-LABEL: test_frame_address
34+
// CIR: [[ARG:%.*]] = cir.const #cir.int<1> : !u32i
35+
// CIR: {{%.*}} = cir.frame_address([[ARG]])
36+
37+
// LLVM-LABEL: @test_frame_address
38+
// LLVM: {{%.*}} = call ptr @llvm.frameaddress.p0(i32 1)
39+
40+
// OGCG-LABEL: @test_frame_address
41+
// OGCG: {{%.*}} = call ptr @llvm.frameaddress.p0(i32 1)
42+
}

0 commit comments

Comments
 (0)