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

Ayokunle Amodu via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 31 06:36:13 PST 2026


https://github.com/ayokunle321 updated https://github.com/llvm/llvm-project/pull/178989

>From eb74487e33c59bb283717e99f92f04665b17ae08 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Thu, 29 Jan 2026 09:55:50 -0700
Subject: [PATCH 1/6] add support for setjmp and longjmp builtins

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 58 +++++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 40 ++++++++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 63 ++++++++++++++
 .../test/CIR/CodeGen/builtin-setjmp-longjmp.c | 84 +++++++++++++++++++
 clang/test/CIR/Lowering/setjmp-longjmp.cir    | 36 ++++++++
 .../CIR/Transforms/setjmp-longjmp-lower.c     | 74 ++++++++++++++++
 6 files changed, 353 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c
 create mode 100644 clang/test/CIR/Lowering/setjmp-longjmp.cir
 create mode 100644 clang/test/CIR/Transforms/setjmp-longjmp-lower.c

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ee84df93b4933..d32dcbd5cdf88 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5895,6 +5895,64 @@ 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.
+
+    The presence of the `builtin` attribute refers to the setjmp() function; the lack of the attribute refers
+    to the _setjmp() function.
+
+    Examples:
+    ```mlir
+      // Specify setjmp is builtin.
+      %0 = cir.eh.setjmp builtin %arg0 : (!cir.ptr<!cir.void>) -> !s32i
+
+      // Specify setjmp is not builtin.
+      %0 = cir.eh.setjmp %arg0 : (!cir.ptr<!cir.void>) -> !s32i
+    ```
+  }];
+  let arguments = (ins CIR_PointerType:$env, UnitAttr:$is_builtin);
+
+  let results = (outs CIR_SInt32:$res);
+
+  let assemblyFormat = [{
+      (`builtin` $is_builtin^)?
+      $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
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // Atomic operations
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 88d37d56fcd78..34d9cf5cda9c2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1335,8 +1335,44 @@ 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 ppTy = builder.getPointerTo(builder.getVoidPtrTy());
+    mlir::Value castBuf = builder.createBitcast(buf.getPointer(), ppTy);
+
+    assert(!cir::MissingFeatures::emitCheckedInBoundsGEP());
+    if (getTarget().getTriple().isSystemZ()) {
+      llvm_unreachable("SYSTEMZ NYI");
+    }
+    
+    mlir::Value frameaddress =
+        cir::FrameAddrOp::create(builder, loc, builder.getVoidPtrTy(),
+                                 mlir::ValueRange{builder.getUInt32(0, loc)})
+            .getResult();
+
+    builder.createStore(loc, frameaddress, Address(castBuf, buf.getAlignment()));
+
+    mlir::Value stacksave =
+        cir::StackSaveOp::create(builder, loc, builder.getVoidPtrTy())
+            .getResult();
+    cir::PtrStrideOp stackSaveSlot = cir::PtrStrideOp::create(
+        builder, loc, ppTy, castBuf, builder.getSInt32(2, loc));
+    builder.createStore(loc, stacksave, Address(stackSaveSlot, buf.getAlignment()));
+    auto op =
+        cir::EhSetjmpOp::create(builder, loc, castBuf, /*is_builtin=*/true);
+    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);
+    builder.create<cir::UnreachableOp>(loc);
+    return RValue::get(nullptr);
+  }
   case Builtin::BI__builtin_launder:
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_sub:
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 091489c404642..97f045e90c7cd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -111,6 +111,39 @@ lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind) {
   }
 }
 
+// Make sure the LLVM function we are about to create a call for actually
+// exists, if not create one. Returns a function
+void getOrCreateLLVMFuncOp(mlir::ConversionPatternRewriter &rewriter,
+                           mlir::Operation *srcOp, llvm::StringRef fnName,
+                           mlir::Type fnTy) {
+  if (!fnTy) {
+    srcOp->emitError("failed to materialize LLVM function type for ") << fnName;
+    return;
+  }
+  auto llvmFnTy = mlir::dyn_cast<mlir::LLVM::LLVMFunctionType>(fnTy);
+  if (!llvmFnTy) {
+    srcOp->emitError("expected LLVM function type for ")
+        << fnName << " but got " << fnTy;
+    return;
+  }
+  auto modOp = srcOp->getParentOfType<mlir::ModuleOp>();
+  if (!modOp) {
+    srcOp->emitError("expected parent module when declaring ") << fnName;
+    return;
+  }
+  auto enclosingFnOp = srcOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
+  if (!enclosingFnOp) {
+    srcOp->emitError("expected parent LLVM function when declaring ") << fnName;
+    return;
+  }
+  auto *sourceSymbol = mlir::SymbolTable::lookupSymbolIn(modOp, fnName);
+  if (!sourceSymbol) {
+    mlir::OpBuilder::InsertionGuard guard(rewriter);
+    rewriter.setInsertionPoint(enclosingFnOp);
+    mlir::LLVM::LLVMFuncOp::create(rewriter, srcOp->getLoc(), fnName, llvmFnTy);
+  }
+}
+
 /// Emits the value from memory as expected by its users. Should be called when
 /// the memory represetnation of a CIR type is not equal to its scalar
 /// representation.
@@ -3613,6 +3646,36 @@ 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());
+  if (op.getIsBuiltin()) {
+    mlir::LLVM::CallIntrinsicOp newOp =
+        createCallLLVMIntrinsicOp(rewriter, op.getLoc(), "llvm.eh.sjlj.setjmp",
+                                  returnType, adaptor.getEnv());
+    rewriter.replaceOp(op, newOp);
+    return mlir::success();
+  }
+
+  StringRef fnName = "_setjmp";
+  auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
+  auto fnType = mlir::LLVM::LLVMFunctionType::get(returnType, llvmPtrTy,
+                                                  /*isVarArg=*/false);
+  getOrCreateLLVMFuncOp(rewriter, op, fnName, fnType);
+  rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(op, returnType, fnName,
+                                                  adaptor.getEnv());
+  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 {
diff --git a/clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c b/clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c
new file mode 100644
index 0000000000000..1cd348993463c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c
@@ -0,0 +1,84 @@
+// 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 [[FA]], [[CAST]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // CIR-NEXT: [[SS:%[0-9]+]] = cir.stack_save
+  // 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 [[SS]], [[GEP]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // CIR-NEXT: [[SJ:%[0-9]+]] = cir.eh.setjmp builtin [[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]]
+  // 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]]
+  // 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]]
+  // OGCG-NEXT: [[SS:%.*]] = {{.*}}@llvm.stacksave.p0() 
+  // OGCG-NEXT: [[GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[ENV]], i64 16
+  // OGCG-NEXT: store ptr [[SS]], ptr [[GEP]]
+  // OGCG-NEXT: @llvm.eh.sjlj.setjmp(ptr{{.*}}[[ENV]])
+  __builtin_setjmp(env);
+}
+
+extern int _setjmp(void *env);
+void test_setjmp2(void *env) {
+  // CIR-LABEL: test_setjmp2
+  // CIR-SAME: [[ENV:%.*]]:
+  // CIR-NEXT: [[ENV_ALLOCA]] = cir.alloca
+  // CIR-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // CIR-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // CIR-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // CIR-NEXT: cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
+
+
+  // LLVM-LABEL: test_setjmp2
+  // LLVM-SAME: (ptr{{.*}}[[ENV:%.*]])
+  // LLVM-NEXT: call i32 @_setjmp(ptr [[ENV]])
+  //
+  // OGCG-LABEL: test_setjmp2
+  // OGCG-SAME: (ptr{{.*}}[[ENV:%.*]])
+  // OGCG: call i32 @_setjmp(ptr noundef [[ENV]])
+  _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);
+}
\ No newline at end of file
diff --git a/clang/test/CIR/Lowering/setjmp-longjmp.cir b/clang/test/CIR/Lowering/setjmp-longjmp.cir
new file mode 100644
index 0000000000000..e7ee21926b533
--- /dev/null
+++ b/clang/test/CIR/Lowering/setjmp-longjmp.cir
@@ -0,0 +1,36 @@
+// RUN: cir-opt %s -cir-to-llvm -o %t.ll
+// RUN: FileCheck %s --input-file=%t.ll -check-prefix=MLIR
+!s32i = !cir.int<s, 32>
+!p32  = !cir.ptr<!s32i>
+
+module {
+  // MLIR: module {
+  cir.func @test_setjmp(%arg0 : !p32) -> !s32i {
+
+    // MLIR:  llvm.func @test_setjmp([[ARG0:%.*]]: !llvm.ptr) -> i32
+    // MLIR-NEXT:    [[RET:%.*]] = llvm.call_intrinsic "llvm.eh.sjlj.setjmp"([[ARG0]]) : (!llvm.ptr) -> i32
+    // MLIR-NEXT:    llvm.return [[RET:%.*]] : i32
+    // MLIR-NEXT:  }
+    %0 = cir.eh.setjmp builtin %arg0 : (!p32) -> !s32i
+    cir.return %0 : !s32i
+  }
+  cir.func @test_setjmp_2(%arg0 : !p32) -> !s32i {
+
+    // MLIR:  llvm.func @test_setjmp_2([[ARG0:%.*]]: !llvm.ptr) -> i32
+    // MLIR-NEXT:    [[RET:%.*]] = llvm.call @_setjmp([[ARG0]]) : (!llvm.ptr) -> i32
+    // MLIR-NEXT:    llvm.return [[RET:%.*]] : i32
+    // MLIR-NEXT:  }
+    %0 = cir.eh.setjmp %arg0 : (!p32) -> !s32i
+    cir.return %0 : !s32i
+  }
+  cir.func @test_longjmp(%arg0 : !p32) {
+
+    // MLIR: llvm.func @test_longjmp([[ARG0:%.*]]: !llvm.ptr)
+    // MLIR-NEXT:    llvm.call_intrinsic "llvm.eh.sjlj.longjmp"([[ARG0]]) : (!llvm.ptr) -> ()
+    // MLIR-NEXT:    llvm.unreachable
+    // MLIR-NEXT:  }
+    cir.eh.longjmp %arg0 : !p32
+    cir.unreachable
+  }
+  // MLIR: }
+}
diff --git a/clang/test/CIR/Transforms/setjmp-longjmp-lower.c b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
new file mode 100644
index 0000000000000..77dd98a3d6778
--- /dev/null
+++ b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o - 2>&1 | FileCheck %s -check-prefix=BEFORE-LOWERING-PREPARE
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o - 2>&1 | FileCheck %s -check-prefix=AFTER-LOWERING-PREPARE
+void test_setjmp(void *env) {
+  // BEFORE-LOWERING-PREPARE-LABEL: test_setjmp
+  // BEFORE-LOWERING-PREPARE-SAME: [[ENV:%.*]]: 
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%[0-9]+]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>,
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%[0-9]+]] = cir.load align(8) [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[CAST:%[0-9]+]] = cir.cast bitcast [[ENV_LOAD]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ZERO:%[0-9]+]] = cir.const #cir.int<0>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[FA:%[0-9]+]] = cir.frame_address([[ZERO]])
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[FA]], [[CAST]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[SS:%[0-9]+]] = cir.stack_save
+  // BEFORE-LOWERING-PREPARE-NEXT: [[TWO:%[0-9]+]] = cir.const #cir.int<2>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[GEP:%[0-9]+]] = cir.ptr_stride [[CAST]], [[TWO]] : (!cir.ptr<!cir.ptr<!void>>, !s32i) -> !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[SS]], [[GEP]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: [[SJ:%[0-9]+]] = cir.eh.setjmp builtin [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
+
+  // AFTER-LOWERING-PREPARE-LABEL: test_setjmp
+  // AFTER-LOWERING-PREPARE-SAME: [[ENV:%.*]]: 
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%[0-9]+]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>,
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%[0-9]+]] = cir.load align(8) [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%[0-9]+]] = cir.cast bitcast [[ENV_LOAD]] : !cir.ptr<!void> -> !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: [[ZERO:%[0-9]+]] = cir.const #cir.int<0>
+  // AFTER-LOWERING-PREPARE-NEXT: [[FA:%[0-9]+]] = cir.frame_address([[ZERO]])
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[FA]], [[CAST]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: [[SS:%[0-9]+]] = cir.stack_save
+  // AFTER-LOWERING-PREPARE-NEXT: [[TWO:%[0-9]+]] = cir.const #cir.int<2>
+  // AFTER-LOWERING-PREPARE-NEXT: [[GEP:%[0-9]+]] = cir.ptr_stride [[CAST]], [[TWO]] : (!cir.ptr<!cir.ptr<!void>>, !s32i) -> !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[SS]], [[GEP]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: [[SJ:%[0-9]+]] = cir.eh.setjmp builtin [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
+  __builtin_setjmp(env);
+}
+
+extern int _setjmp(void *env);
+void test_setjmp2(void *env) {
+  // BEFORE-LOWERING-PREPARE-LABEL: test_setjmp2
+  // BEFORE-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
+
+  // AFTER-LOWERING-PREPARE-LABEL: test_setjmp2
+  // AFTER-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // AFTER-LOWERING-PREPARE-NEXT: cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
+  _setjmp (env);
+}
+void test_longjmp(void *env) {
+  // BEFORE-LOWERING-PREPARE-LABEL: test_longjmp
+  // BEFORE-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.unreachable
+
+  // AFTER-LOWERING-PREPARE-LABEL: test_longjmp
+  // AFTER-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // AFTER-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: cir.unreachable
+  __builtin_longjmp(env, 1);
+}
\ No newline at end of file

>From c436f0589a10a3e6f881220c69d56b7be307e677 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 30 Jan 2026 15:31:55 -0700
Subject: [PATCH 2/6] remove longjmp handling

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 32 +++---------------
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  1 +
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 33 ++++++++++++-------
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  8 -----
 .../builtin-setjmp-longjmp.c                  | 21 ------------
 clang/test/CIR/Lowering/setjmp-longjmp.cir    |  9 -----
 .../CIR/Transforms/setjmp-longjmp-lower.c     | 20 -----------
 7 files changed, 27 insertions(+), 97 deletions(-)
 rename clang/test/CIR/{CodeGen => CodeGenBuiltins}/builtin-setjmp-longjmp.c (79%)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d32dcbd5cdf88..82d733a535444 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3629,7 +3629,7 @@ def CIR_AddrOfReturnAddrOp : CIR_Op<"address_of_return_address"> {
 // StackSaveOp & StackRestoreOp
 //===----------------------------------------------------------------------===//
 
-def CIR_StackSaveOp : CIR_Op<"stacksave"> {
+def CIR_StackSaveOp : CIR_Op<"stack_save"> {
   let summary = "remembers the current state of the function stack";
   let description = [{
     Saves current state of the function stack. Returns a pointer to an opaque object
@@ -3639,7 +3639,7 @@ def CIR_StackSaveOp : CIR_Op<"stacksave"> {
     This operation corresponds to LLVM intrinsic `stacksave`.
 
     ```mlir
-    %0 = cir.stacksave : <!u8i>
+    %0 = cir.stack_save : <!u8i>
     ```
   }];
 
@@ -3651,14 +3651,14 @@ def CIR_StackRestoreOp : CIR_Op<"stackrestore"> {
   let summary = "restores the state of the function stack";
   let description = [{
     Restore the state of the function stack to the state it was
-    in when the corresponding cir.stacksave executed.
+    in when the corresponding cir.stack_save executed.
     This is used during the lowering of variable length array allocas.
 
     This operation corresponds to LLVM intrinsic `stackrestore`.
 
     ```mlir
     %0 = cir.alloca !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"] {alignment = 8 : i64}
-    %1 = cir.stacksave : <!u8i>
+    %1 = cir.stack_save : <!u8i>
     cir.store %1, %0 : !cir.ptr<!u8i>, !cir.ptr<!cir.ptr<!u8i>>
     %2 = cir.load %0 : !cir.ptr<!cir.ptr<!u8i>>, !cir.ptr<!u8i>
     cir.stackrestore %2 : !cir.ptr<!u8i>
@@ -5929,30 +5929,6 @@ def CIR_EhSetjmpOp : CIR_Op<"eh.setjmp"> {
   }];
 }
 
-//===----------------------------------------------------------------------===//
-// 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
-  }];
-}
-
 //===----------------------------------------------------------------------===//
 // Atomic operations
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index dedb369bf3f67..ab36b67995261 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -519,6 +519,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     return createAlignedLoad(loc, ty, ptr, align.getAsAlign());
   }
 
+  using CIRBaseBuilderTy::createStore;
   cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst,
                            bool isVolatile = false,
                            mlir::IntegerAttr align = {},
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 34d9cf5cda9c2..25b3eef652d96 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1353,26 +1353,19 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
                                  mlir::ValueRange{builder.getUInt32(0, loc)})
             .getResult();
 
-    builder.createStore(loc, frameaddress, Address(castBuf, buf.getAlignment()));
+    builder.createStore(loc, frameaddress, castBuf);
 
     mlir::Value stacksave =
         cir::StackSaveOp::create(builder, loc, builder.getVoidPtrTy())
             .getResult();
     cir::PtrStrideOp stackSaveSlot = cir::PtrStrideOp::create(
         builder, loc, ppTy, castBuf, builder.getSInt32(2, loc));
-    builder.createStore(loc, stacksave, Address(stackSaveSlot, buf.getAlignment()));
+    builder.createStore(loc, stacksave, stackSaveSlot);
     auto op =
         cir::EhSetjmpOp::create(builder, loc, castBuf, /*is_builtin=*/true);
     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);
-    builder.create<cir::UnreachableOp>(loc);
-    return RValue::get(nullptr);
-  }
+  case Builtin::BI__builtin_longjmp:
   case Builtin::BI__builtin_launder:
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_sub:
@@ -1773,7 +1766,25 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
   case Builtin::BI__abnormal_termination:
   case Builtin::BI_abnormal_termination:
   case Builtin::BI_setjmpex:
-  case Builtin::BI_setjmp:
+    return errorBuiltinNYI(*this, e, builtinID);
+  case Builtin::BI_setjmp: {
+    if (getTarget().getTriple().isOSMSVCRT() && e->getNumArgs() == 1 &&
+        e->getArg(0)->getType()->isPointerType()) {
+      if (getTarget().getTriple().getArch() == llvm::Triple::x86)
+        llvm_unreachable("NYI setjmp on x86");
+      else if (getTarget().getTriple().getArch() == llvm::Triple::aarch64){
+        llvm_unreachable("NYI setjmp on aarch64");
+      }
+      llvm_unreachable("NYI setjmp on generic MSVCRT");
+    }
+    Address buf = emitPointerWithAlignment(e->getArg(0));
+    mlir::Location loc = getLoc(e->getExprLoc());
+    cir::PointerType ppTy = builder.getPointerTo(builder.getVoidPtrTy());
+    mlir::Value castBuf = builder.createBitcast(buf.getPointer(), ppTy);
+    auto op =
+        cir::EhSetjmpOp::create(builder, loc, castBuf, /*is_builtin = */ false);
+    return RValue::get(op);
+  }
   case Builtin::BImove:
   case Builtin::BImove_if_noexcept:
   case Builtin::BIforward:
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 97f045e90c7cd..e8ca27241d245 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3668,14 +3668,6 @@ mlir::LogicalResult CIRToLLVMEhSetjmpOpLowering::matchAndRewrite(
   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 {
diff --git a/clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c b/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
similarity index 79%
rename from clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c
rename to clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
index 1cd348993463c..782f68e003774 100644
--- a/clang/test/CIR/CodeGen/builtin-setjmp-longjmp.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
@@ -60,25 +60,4 @@ void test_setjmp2(void *env) {
   // OGCG-SAME: (ptr{{.*}}[[ENV:%.*]])
   // OGCG: call i32 @_setjmp(ptr noundef [[ENV]])
   _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);
 }
\ No newline at end of file
diff --git a/clang/test/CIR/Lowering/setjmp-longjmp.cir b/clang/test/CIR/Lowering/setjmp-longjmp.cir
index e7ee21926b533..ff6551596a6bf 100644
--- a/clang/test/CIR/Lowering/setjmp-longjmp.cir
+++ b/clang/test/CIR/Lowering/setjmp-longjmp.cir
@@ -23,14 +23,5 @@ module {
     %0 = cir.eh.setjmp %arg0 : (!p32) -> !s32i
     cir.return %0 : !s32i
   }
-  cir.func @test_longjmp(%arg0 : !p32) {
-
-    // MLIR: llvm.func @test_longjmp([[ARG0:%.*]]: !llvm.ptr)
-    // MLIR-NEXT:    llvm.call_intrinsic "llvm.eh.sjlj.longjmp"([[ARG0]]) : (!llvm.ptr) -> ()
-    // MLIR-NEXT:    llvm.unreachable
-    // MLIR-NEXT:  }
-    cir.eh.longjmp %arg0 : !p32
-    cir.unreachable
-  }
   // MLIR: }
 }
diff --git a/clang/test/CIR/Transforms/setjmp-longjmp-lower.c b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
index 77dd98a3d6778..1f7b8a52f4386 100644
--- a/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
+++ b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
@@ -51,24 +51,4 @@ void test_setjmp2(void *env) {
   // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
   // AFTER-LOWERING-PREPARE-NEXT: cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
   _setjmp (env);
-}
-void test_longjmp(void *env) {
-  // BEFORE-LOWERING-PREPARE-LABEL: test_longjmp
-  // BEFORE-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
-  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
-  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
-  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
-  // BEFORE-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
-  // BEFORE-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
-  // BEFORE-LOWERING-PREPARE-NEXT: cir.unreachable
-
-  // AFTER-LOWERING-PREPARE-LABEL: test_longjmp
-  // AFTER-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
-  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
-  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
-  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
-  // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
-  // AFTER-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
-  // AFTER-LOWERING-PREPARE-NEXT: cir.unreachable
-  __builtin_longjmp(env, 1);
 }
\ No newline at end of file

>From d9e3c9ab5732e9f093159c9b83ff334184192b75 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 30 Jan 2026 16:28:31 -0700
Subject: [PATCH 3/6] fix code format

---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 25b3eef652d96..0b8f109f09ae5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1772,7 +1772,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
         e->getArg(0)->getType()->isPointerType()) {
       if (getTarget().getTriple().getArch() == llvm::Triple::x86)
         llvm_unreachable("NYI setjmp on x86");
-      else if (getTarget().getTriple().getArch() == llvm::Triple::aarch64){
+      else if (getTarget().getTriple().getArch() == llvm::Triple::aarch64) {
         llvm_unreachable("NYI setjmp on aarch64");
       }
       llvm_unreachable("NYI setjmp on generic MSVCRT");

>From 0e0494565150e6be81c12225e0f99c56ffbc47c5 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 30 Jan 2026 16:42:10 -0700
Subject: [PATCH 4/6] fix code format

---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 0b8f109f09ae5..7fee1a8495fc9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1347,7 +1347,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
     if (getTarget().getTriple().isSystemZ()) {
       llvm_unreachable("SYSTEMZ NYI");
     }
-    
+
     mlir::Value frameaddress =
         cir::FrameAddrOp::create(builder, loc, builder.getVoidPtrTy(),
                                  mlir::ValueRange{builder.getUInt32(0, loc)})

>From 6abcae6d14d81aa7198eb580ecffd7b83a65f99b Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 30 Jan 2026 19:04:20 -0700
Subject: [PATCH 5/6] add support for longjmp

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 24 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       |  9 ++++++-
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  8 +++++++
 .../CodeGenBuiltins/builtin-setjmp-longjmp.c  | 21 ++++++++++++++++
 clang/test/CIR/Lowering/setjmp-longjmp.cir    |  9 +++++++
 .../CIR/Transforms/setjmp-longjmp-lower.c     | 21 ++++++++++++++++
 6 files changed, 91 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 82d733a535444..ed7a213dc9624 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5929,6 +5929,30 @@ def CIR_EhSetjmpOp : CIR_Op<"eh.setjmp"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// 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
+  }];
+}
+
 //===----------------------------------------------------------------------===//
 // Atomic operations
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 7fee1a8495fc9..91b79c704b3bf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1365,7 +1365,14 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
         cir::EhSetjmpOp::create(builder, loc, castBuf, /*is_builtin=*/true);
     return RValue::get(op);
   }
-  case Builtin::BI__builtin_longjmp:
+  case Builtin::BI__builtin_longjmp: {
+    mlir::Value buf = emitScalarExpr(e->getArg(0));
+    mlir::Location loc = getLoc(e->getExprLoc());
+
+    cir::EhLongjmpOp::create(builder, loc, buf);
+    builder.create<cir::UnreachableOp>(loc);
+    return RValue::get(nullptr);
+  }
   case Builtin::BI__builtin_launder:
   case Builtin::BI__sync_fetch_and_add:
   case Builtin::BI__sync_fetch_and_sub:
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e8ca27241d245..97f045e90c7cd 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3668,6 +3668,14 @@ mlir::LogicalResult CIRToLLVMEhSetjmpOpLowering::matchAndRewrite(
   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 {
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c b/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
index 782f68e003774..1cd348993463c 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-setjmp-longjmp.c
@@ -60,4 +60,25 @@ void test_setjmp2(void *env) {
   // OGCG-SAME: (ptr{{.*}}[[ENV:%.*]])
   // OGCG: call i32 @_setjmp(ptr noundef [[ENV]])
   _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);
 }
\ No newline at end of file
diff --git a/clang/test/CIR/Lowering/setjmp-longjmp.cir b/clang/test/CIR/Lowering/setjmp-longjmp.cir
index ff6551596a6bf..e7ee21926b533 100644
--- a/clang/test/CIR/Lowering/setjmp-longjmp.cir
+++ b/clang/test/CIR/Lowering/setjmp-longjmp.cir
@@ -23,5 +23,14 @@ module {
     %0 = cir.eh.setjmp %arg0 : (!p32) -> !s32i
     cir.return %0 : !s32i
   }
+  cir.func @test_longjmp(%arg0 : !p32) {
+
+    // MLIR: llvm.func @test_longjmp([[ARG0:%.*]]: !llvm.ptr)
+    // MLIR-NEXT:    llvm.call_intrinsic "llvm.eh.sjlj.longjmp"([[ARG0]]) : (!llvm.ptr) -> ()
+    // MLIR-NEXT:    llvm.unreachable
+    // MLIR-NEXT:  }
+    cir.eh.longjmp %arg0 : !p32
+    cir.unreachable
+  }
   // MLIR: }
 }
diff --git a/clang/test/CIR/Transforms/setjmp-longjmp-lower.c b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
index 1f7b8a52f4386..376e3d729b7f5 100644
--- a/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
+++ b/clang/test/CIR/Transforms/setjmp-longjmp-lower.c
@@ -51,4 +51,25 @@ void test_setjmp2(void *env) {
   // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
   // AFTER-LOWERING-PREPARE-NEXT: cir.eh.setjmp [[CAST]] : (!cir.ptr<!cir.ptr<!void>>) -> !s32i
   _setjmp (env);
+}
+
+void test_longjmp(void *env) {
+  // BEFORE-LOWERING-PREPARE-LABEL: test_longjmp
+  // BEFORE-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // BEFORE-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
+  // BEFORE-LOWERING-PREPARE-NEXT: cir.unreachable
+
+  // AFTER-LOWERING-PREPARE-LABEL: test_longjmp
+  // AFTER-LOWERING-PREPARE-SAME: [[ENV:%.*]]:
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_ALLOCA:%.*]] = cir.alloca
+  // AFTER-LOWERING-PREPARE-NEXT: cir.store [[ENV]], [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[ENV_LOAD:%.*]] = cir.load align(8) [[ENV_ALLOCA]]
+  // AFTER-LOWERING-PREPARE-NEXT: [[CAST:%.*]] = cir.cast bitcast [[ENV_LOAD]]
+  // AFTER-LOWERING-PREPARE-NEXT: cir.eh.longjmp [[CAST]] : !cir.ptr<!cir.ptr<!void>>
+  // AFTER-LOWERING-PREPARE-NEXT: cir.unreachable
+  __builtin_longjmp(env, 1);
 }
\ No newline at end of file

>From a264c437ad322901fb8fdcc16371e829a68f0c47 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Sat, 31 Jan 2026 07:35:46 -0700
Subject: [PATCH 6/6] replace builder create method with op's create method

---
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 91b79c704b3bf..d5ea7820a2054 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1370,7 +1370,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
     mlir::Location loc = getLoc(e->getExprLoc());
 
     cir::EhLongjmpOp::create(builder, loc, buf);
-    builder.create<cir::UnreachableOp>(loc);
+    cir::UnreachableOp::create(builder, loc);
     return RValue::get(nullptr);
   }
   case Builtin::BI__builtin_launder:



More information about the cfe-commits mailing list