[CIR] Upstream support for setjmp & longjmp builtins (#178989)

This adds support in CIR for the setjmp & longjmp builtins.
This commit is contained in:
Ayokunle Amodu 2026-02-17 11:50:40 -07:00 committed by GitHub
parent e09a587440
commit 7edf569ce6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 176 additions and 2 deletions

View File

@ -6372,6 +6372,57 @@ def CIR_EhTypeIdOp : CIR_Op<"eh.typeid",
}];
}
//===----------------------------------------------------------------------===//
// Exception related: EhSetjmpOp
//===----------------------------------------------------------------------===//
def CIR_EhSetjmpOp : CIR_Op<"eh.setjmp"> {
let summary = "CIR setjmp operation";
let description = [{
Saves call-site information (e.g., stack pointer, instruction pointer,
signal mask, and other registers) in memory at `env` for use by longjmp().
In this case, setjmp() returns 0. Following a successful longjmp(),
execution proceeds from cir.eh.setjmp with the operation yielding a
non-zero value.
Examples:
```mlir
%0 = cir.eh.setjmp %arg0 : (!cir.ptr<!cir.void>) -> !s32i
```
}];
let arguments = (ins CIR_PointerType:$env);
let results = (outs CIR_SInt32:$res);
let assemblyFormat = [{
$env `:` functional-type($env, results) attr-dict
}];
}
//===----------------------------------------------------------------------===//
// Exception related: EhLongjmpOp
//===----------------------------------------------------------------------===//
def CIR_EhLongjmpOp : CIR_Op<"eh.longjmp"> {
let summary = "CIR longjmp operation";
let description = [{
Restore the environment (e.g., stack pointer, instruction pointer,
signal mask, and other registers) at the time of setjmp() call, by using
the information saved in `env` by setjmp().
Examples:
```mlir
cir.eh.longjmp %arg0 : !cir.ptr<!cir.void>
```
}];
let arguments = (ins CIR_PointerType:$env);
let assemblyFormat = [{
$env `:` qualified(type($env)) attr-dict
}];
}
//===----------------------------------------------------------------------===//
// Flattened EH Operations: EhInitiateOp
//===----------------------------------------------------------------------===//

View File

@ -1563,8 +1563,49 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__builtin_eh_return:
case Builtin::BI__builtin_unwind_init:
case Builtin::BI__builtin_extend_pointer:
case Builtin::BI__builtin_setjmp:
case Builtin::BI__builtin_longjmp:
return errorBuiltinNYI(*this, e, builtinID);
case Builtin::BI__builtin_setjmp: {
Address buf = emitPointerWithAlignment(e->getArg(0));
mlir::Location loc = getLoc(e->getExprLoc());
cir::PointerType voidPtrTy = builder.getVoidPtrTy();
cir::PointerType ppTy = builder.getPointerTo(voidPtrTy);
Address castBuf = buf.withElementType(builder, voidPtrTy);
assert(!cir::MissingFeatures::emitCheckedInBoundsGEP());
if (getTarget().getTriple().isSystemZ()) {
cgm.errorNYI(e->getExprLoc(), "setjmp on SystemZ");
return {};
}
mlir::Value frameAddress =
cir::FrameAddrOp::create(builder, loc, voidPtrTy,
mlir::ValueRange{builder.getUInt32(0, loc)})
.getResult();
builder.createStore(loc, frameAddress, castBuf);
mlir::Value stacksave =
cir::StackSaveOp::create(builder, loc, voidPtrTy).getResult();
cir::PtrStrideOp stackSaveSlot = cir::PtrStrideOp::create(
builder, loc, ppTy, castBuf.getPointer(), builder.getSInt32(2, loc));
llvm::TypeSize voidPtrTySize =
cgm.getDataLayout().getTypeAllocSize(voidPtrTy);
CharUnits slotAlign = castBuf.getAlignment().alignmentAtOffset(
CharUnits().fromQuantity(2 * voidPtrTySize));
Address slotAddr = Address(stackSaveSlot, voidPtrTy, slotAlign);
builder.createStore(loc, stacksave, slotAddr);
auto op = cir::EhSetjmpOp::create(builder, loc, castBuf.getPointer());
return RValue::get(op);
}
case Builtin::BI__builtin_longjmp: {
mlir::Value buf = emitScalarExpr(e->getArg(0));
mlir::Location loc = getLoc(e->getExprLoc());
cir::EhLongjmpOp::create(builder, loc, buf);
cir::UnreachableOp::create(builder, loc);
return RValue::get(nullptr);
}
case Builtin::BI__builtin_launder:
case Builtin::BI__sync_fetch_and_add:
case Builtin::BI__sync_fetch_and_sub:

View File

@ -3853,6 +3853,25 @@ mlir::LogicalResult CIRToLLVMEhTypeIdOpLowering::matchAndRewrite(
return mlir::success();
}
mlir::LogicalResult CIRToLLVMEhSetjmpOpLowering::matchAndRewrite(
cir::EhSetjmpOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type returnType = typeConverter->convertType(op.getType());
mlir::LLVM::CallIntrinsicOp newOp =
createCallLLVMIntrinsicOp(rewriter, op.getLoc(), "llvm.eh.sjlj.setjmp",
returnType, adaptor.getEnv());
rewriter.replaceOp(op, newOp);
return mlir::success();
}
mlir::LogicalResult CIRToLLVMEhLongjmpOpLowering::matchAndRewrite(
cir::EhLongjmpOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.eh.sjlj.longjmp",
/*resultTy=*/{}, adaptor.getOperands());
return mlir::success();
}
mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
cir::TrapOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {

View File

@ -0,0 +1,63 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux -O2 -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -O2 -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
// RUN: %clang_cc1 -triple x86_64-unknown-linux -O2 -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
void test_setjmp(void *env) {
// CIR-LABEL: test_setjmp
// CIR-SAME: [[ENV:%.*]]:
// CIR-NEXT: [[ENV_ALLOCA:%[0-9]+]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>,
// CIR-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: [[ENV_LOAD:%[0-9]+]] = cir.load align(8) [[ENV_ALLOCA]]
// CIR-NEXT: [[CAST:%[0-9]+]] = cir.cast bitcast [[ENV_LOAD]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: [[ZERO:%[0-9]+]] = cir.const #cir.int<0>
// CIR-NEXT: [[FA:%[0-9]+]] = cir.frame_address([[ZERO]])
// CIR-NEXT: cir.store align(8) [[FA]], [[CAST]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: [[SS:%[0-9]+]] = cir.stacksave
// CIR-NEXT: [[TWO:%[0-9]+]] = cir.const #cir.int<2>
// CIR-NEXT: [[GEP:%[0-9]+]] = cir.ptr_stride [[CAST]], [[TWO]] : (!cir.ptr<!cir.ptr<!void>>, !s32i) -> !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: cir.store align(8) [[SS]], [[GEP]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: [[SJ:%[0-9]+]] = cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
// LLVM-LABEL: test_setjmp
// LLVM-SAME: (ptr{{.*}}[[ENV:%.*]])
// LLVM-NEXT: [[FA:%[0-9]+]] = {{.*}}@llvm.frameaddress.p0(i32 0)
// LLVM-NEXT: store ptr [[FA]], ptr [[ENV]], align 8
// LLVM-NEXT: [[SS:%[0-9]+]] = {{.*}}@llvm.stacksave.p0()
// LLVM-NEXT: [[GEP:%[0-9]+]] = getelementptr i8, ptr [[ENV]], i64 16
// LLVM-NEXT: store ptr [[SS]], ptr [[GEP]], align 8
// LLVM-NEXT: @llvm.eh.sjlj.setjmp(ptr{{.*}}[[ENV]])
// OGCG-LABEL: test_setjmp
// OGCG-SAME: (ptr{{.*}}[[ENV:%.*]])
// OGCG: [[FA:%.*]] = {{.*}}@llvm.frameaddress.p0(i32 0)
// OGCG-NEXT: store ptr [[FA]], ptr [[ENV]], align 8
// OGCG-NEXT: [[SS:%.*]] = {{.*}}@llvm.stacksave.p0()
// OGCG-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[ENV]], i64 16
// OGCG-NEXT: store ptr [[SS]], ptr [[GEP]], align 8
// OGCG-NEXT: @llvm.eh.sjlj.setjmp(ptr{{.*}}[[ENV]])
__builtin_setjmp(env);
}
void test_longjmp(void *env) {
// CIR-LABEL: test_longjmp
// CIR-SAME: [[ENV:%.*]]:
// CIR-NEXT: [[ENV_ALLOCA:%[0-9]+]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>,
// CIR-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: [[ENV_LOAD:%[0-9]+]] = cir.load align(8) [[ENV_ALLOCA]]
// CIR-NEXT: [[CAST:%[0-9]+]] = cir.cast bitcast [[ENV_LOAD]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
// CIR-NEXT: cir.unreachable
// LLVM-LABEL: test_longjmp
// LLVM: @llvm.eh.sjlj.longjmp
// LLVM-NEXT: unreachable
// OGCG-LABEL: test_longjmp
// OGCG: @llvm.eh.sjlj.longjmp
// OGCG-NEXT: unreachable
__builtin_longjmp(env, 1);
}