Skip to content

Commit 1de9842

Browse files
committed
[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.
1 parent e56ae96 commit 1de9842

File tree

8 files changed

+206
-2
lines changed

8 files changed

+206
-2
lines changed

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,67 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
22102210
];
22112211
}
22122212

2213+
//===----------------------------------------------------------------------===//
2214+
// ReturnAddrOp and FrameAddrOp
2215+
//===----------------------------------------------------------------------===//
2216+
2217+
class CIR_FuncAddrBuiltinOp<string mnemonic> : CIR_Op<mnemonic, []> {
2218+
let arguments = (ins CIR_UInt32:$level);
2219+
let results = (outs CIR_VoidPtrType:$result);
2220+
let assemblyFormat = [{
2221+
`(` $level `)` attr-dict
2222+
}];
2223+
}
2224+
2225+
def CIR_ReturnAddrOp : CIR_FuncAddrBuiltinOp<"return_address"> {
2226+
let summary =
2227+
"The return address of the current function, or of one of its callers";
2228+
2229+
let description = [{
2230+
Represents call to builtin function ` __builtin_return_address` in CIR.
2231+
This builtin function returns the return address of the current function,
2232+
or of one of its callers.
2233+
The `level` argument is number of frames to scan up the call stack.
2234+
For instance, value of 0 yields the return address of the current function,
2235+
value of 1 yields the return address of the caller of the current function,
2236+
and so forth.
2237+
2238+
Examples:
2239+
2240+
```mlir
2241+
%p = return_address(%level) -> !cir.ptr<!void>
2242+
```
2243+
}];
2244+
}
2245+
2246+
def CIR_FrameAddrOp : CIR_FuncAddrBuiltinOp<"frame_address"> {
2247+
let summary =
2248+
"The frame address of the current function, or of one of its callers";
2249+
2250+
let description = [{
2251+
Represents call to builtin function ` __builtin_frame_address` in CIR.
2252+
This builtin function returns the frame address of the current function,
2253+
or of one of its callers. The frame is the area on the stack that holds
2254+
local variables and saved registers. The frame address is normally the
2255+
address of the first word pushed on to the stack by the function.
2256+
However, the exact definition depends upon the processor and the calling
2257+
convention. If the processor has a dedicated frame pointer register, and
2258+
the function has a frame, then __builtin_frame_address returns the value of
2259+
the frame pointer register.
2260+
2261+
The `level` argument is number of frames to scan up the call stack.
2262+
For instance, value of 0 yields the frame address of the current function,
2263+
value of 1 yields the frame address of the caller of the current function,
2264+
and so forth.
2265+
2266+
Examples:
2267+
2268+
```mlir
2269+
%p = frame_address(%level) -> !cir.ptr<!void>
2270+
```
2271+
}];
2272+
}
2273+
22132274
//===----------------------------------------------------------------------===//
22142275
// StackSaveOp & StackRestoreOp
22152276
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
262262
cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
263263
return getConstantInt(loc, getSInt32Ty(), c);
264264
}
265+
cir::ConstantOp getUInt32(uint32_t c, mlir::Location loc) {
266+
return getConstantInt(loc, getUInt32Ty(), c);
267+
}
265268

266269
// Creates constant nullptr for pointer type ty.
267270
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+
mlir::Attribute levelAttr = ConstantEmitter(*this).emitAbstract(
319+
e->getArg(0), e->getArg(0)->getType());
320+
uint64_t level = mlir::cast<cir::IntAttr>(levelAttr).getUInt();
321+
if (builtinID == Builtin::BI__builtin_return_address) {
322+
return RValue::get(builder.create<cir::ReturnAddrOp>(
323+
loc, builder.getUInt32(level, loc)));
324+
}
325+
return RValue::get(
326+
builder.create<cir::FrameAddrOp>(loc, builder.getUInt32(level, loc)));
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
@@ -700,6 +700,16 @@ mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
700700
return (c ? emitForMemory(c, destType) : nullptr);
701701
}
702702

703+
mlir::Attribute ConstantEmitter::emitAbstract(const Expr *e,
704+
QualType destType) {
705+
AbstractStateRAII state{*this, true};
706+
mlir::Attribute c = mlir::cast<mlir::Attribute>(tryEmitPrivate(e, destType));
707+
if (!c)
708+
cgm.errorNYI(e->getSourceRange(),
709+
"emitAbstract failed, emit null constaant");
710+
return c;
711+
}
712+
703713
mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
704714
const APValue &value,
705715
QualType destType) {
@@ -721,6 +731,32 @@ mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
721731
return c;
722732
}
723733

734+
mlir::TypedAttr ConstantEmitter::tryEmitPrivate(const Expr *e,
735+
QualType destType) {
736+
assert(!destType->isVoidType() && "can't emit a void constant");
737+
738+
if (mlir::Attribute c =
739+
ConstExprEmitter(*this).Visit(const_cast<Expr *>(e), destType))
740+
return llvm::dyn_cast<mlir::TypedAttr>(c);
741+
742+
Expr::EvalResult result;
743+
744+
bool success = false;
745+
746+
if (destType->isReferenceType())
747+
success = e->EvaluateAsLValue(result, cgm.getASTContext());
748+
else
749+
success =
750+
e->EvaluateAsRValue(result, cgm.getASTContext(), inConstantContext);
751+
752+
if (success && !result.hasSideEffects()) {
753+
mlir::Attribute c = tryEmitPrivate(result.Val, destType);
754+
return llvm::dyn_cast<mlir::TypedAttr>(c);
755+
}
756+
757+
return nullptr;
758+
}
759+
724760
mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
725761
QualType destType) {
726762
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 rewriter.create<mlir::LLVM::CallIntrinsicOp>(
277+
loc, resultTy, 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();
@@ -1097,6 +1117,24 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
10971117
getTypeConverter(), op.getCalleeAttr());
10981118
}
10991119

1120+
mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
1121+
cir::ReturnAddrOp op, OpAdaptor adaptor,
1122+
mlir::ConversionPatternRewriter &rewriter) const {
1123+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
1124+
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress",
1125+
llvmPtrTy, adaptor.getOperands());
1126+
return mlir::success();
1127+
}
1128+
1129+
mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
1130+
cir::FrameAddrOp op, OpAdaptor adaptor,
1131+
mlir::ConversionPatternRewriter &rewriter) const {
1132+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
1133+
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.frameaddress", llvmPtrTy,
1134+
adaptor.getOperands());
1135+
return mlir::success();
1136+
}
1137+
11001138
mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
11011139
cir::LoadOp op, OpAdaptor adaptor,
11021140
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2307,10 +2345,12 @@ void ConvertCIRToLLVMPass::runOnOperation() {
23072345
CIRToLLVMConstantOpLowering,
23082346
CIRToLLVMExpectOpLowering,
23092347
CIRToLLVMFAbsOpLowering,
2348+
CIRToLLVMFrameAddrOpLowering,
23102349
CIRToLLVMFuncOpLowering,
23112350
CIRToLLVMGetBitfieldOpLowering,
23122351
CIRToLLVMGetGlobalOpLowering,
23132352
CIRToLLVMGetMemberOpLowering,
2353+
CIRToLLVMReturnAddrOpLowering,
23142354
CIRToLLVMRotateOpLowering,
23152355
CIRToLLVMSelectOpLowering,
23162356
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)