[clang] [CIR] Upstream Re-Throw with no return (PR #154994)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 22 10:29:15 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Amr Hesham (AmrDeveloper)
<details>
<summary>Changes</summary>
This change adds support for the throw op without sub expression and with noreturn
Issue #<!-- -->154992
---
Full diff: https://github.com/llvm/llvm-project/pull/154994.diff
11 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+20-1)
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+54)
- (modified) clang/lib/CIR/CodeGen/CIRGenCXXABI.h (+3-1)
- (added) clang/lib/CIR/CodeGen/CIRGenException.cpp (+41)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+5)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+2)
- (modified) clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp (+49-1)
- (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+18)
- (modified) clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp (+28-8)
- (added) clang/test/CIR/CodeGen/exceptions.cpp (+19)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index d29e5687d2544..aba4615b2a7f4 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -19,7 +19,6 @@
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinAttributes.h"
-#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Types.h"
@@ -313,6 +312,26 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
resOperands, attrs);
}
+ cir::CallOp
+ createTryCallOp(mlir::Location loc,
+ mlir::SymbolRefAttr callee = mlir::SymbolRefAttr(),
+ mlir::Type returnType = cir::VoidType(),
+ mlir::ValueRange operands = mlir::ValueRange(),
+ cir::SideEffect sideEffect = cir::SideEffect::All) {
+ assert(!cir::MissingFeatures::opCallCallConv());
+ return createCallOp(loc, callee, returnType, operands);
+ }
+
+ cir::CallOp
+ createTryCallOp(mlir::Location loc, cir::FuncOp callee,
+ mlir::ValueRange operands,
+ cir::SideEffect sideEffect = cir::SideEffect::All) {
+ assert(!cir::MissingFeatures::opCallCallConv());
+ return createTryCallOp(loc, mlir::SymbolRefAttr::get(callee),
+ callee.getFunctionType().getReturnType(), operands,
+ sideEffect);
+ }
+
//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index f237642700924..708dd35694913 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3757,4 +3757,58 @@ def CIR_VAArgOp : CIR_Op<"va_arg"> {
}];
}
+//===----------------------------------------------------------------------===//
+// ThrowOp
+//===----------------------------------------------------------------------===//
+
+def CIR_ThrowOp : CIR_Op<"throw"> {
+ let summary = "(Re)Throws an exception";
+ let description = [{
+ Very similar to __cxa_throw:
+
+ ```
+ void __cxa_throw(void *thrown_exception, std::type_info *tinfo,
+ void (*dest) (void *));
+ ```
+
+ The absense of arguments for `cir.throw` means it rethrows.
+
+ For the no-rethrow version, it must have at least two operands, the RTTI
+ information, a pointer to the exception object (likely allocated via
+ `cir.cxa.allocate_exception`) and finally an optional dtor, which might
+ run as part of this operation.
+
+ Example:
+ ```mlir
+ // throw;
+ cir.throw
+
+ // if (b == 0)
+ // throw "Division by zero condition!";
+ cir.if %cond {
+ %exception_addr = cir.alloc_exception 8 -> !cir.ptr<!void>
+ ...
+ cir.store %string_addr, %exception_addr : // Store string addr for "Division by zero condition!"
+ cir.throw %exception_addr : !cir.ptr<!cir.ptr<!u8i>>, @"typeinfo for char const*"
+ ```
+ }];
+
+ let arguments = (ins Optional<CIR_PointerType>:$exception_ptr,
+ OptionalAttr<FlatSymbolRefAttr>:$type_info,
+ OptionalAttr<FlatSymbolRefAttr>:$dtor);
+
+ let assemblyFormat = [{
+ ($exception_ptr^ `:` type($exception_ptr))?
+ (`,` $type_info^)?
+ (`,` $dtor^)?
+ attr-dict
+ }];
+
+ let extraClassDeclaration = [{
+ bool rethrows() { return getNumOperands() == 0; }
+ }];
+
+ let hasVerifier = 1;
+}
+
#endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 3f1cb8363a556..104844859382c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -47,9 +47,11 @@ class CIRGenCXXABI {
}
/// Emit the ABI-specific prolog for the function
- virtual void emitInstanceFunctionProlog(SourceLocation Loc,
+ virtual void emitInstanceFunctionProlog(SourceLocation loc,
CIRGenFunction &cgf) = 0;
+ virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0;
+
/// Get the type of the implicit "this" parameter used by a method. May return
/// zero if no specific type is applicable, e.g. if the ABI expects the "this"
/// parameter to point to some artificial offset in a complete object due to
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
new file mode 100644
index 0000000000000..7fcb39a2b74c4
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -0,0 +1,41 @@
+//===--- CIRGenException.cpp - Emit CIR Code for C++ exceptions -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code dealing with C++ exception related code generation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenCXXABI.h"
+#include "CIRGenFunction.h"
+
+#include "clang/AST/StmtVisitor.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
+ const llvm::Triple &triple = getTarget().getTriple();
+ if (cgm.getLangOpts().OpenMPIsTargetDevice &&
+ (triple.isNVPTX() || triple.isAMDGCN())) {
+ cgm.errorNYI("emitCXXThrowExpr OpenMP with NVPTX or AMDGCN Triples");
+ return;
+ }
+
+ if (const Expr *subExpr = e->getSubExpr()) {
+ QualType throwType = subExpr->getType();
+ if (throwType->isObjCObjectPointerType()) {
+ cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
+ return;
+ } else {
+ cgm.errorNYI("emitCXXThrowExpr with subExpr");
+ return;
+ }
+ } else {
+ cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
+ }
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index f6b2c88f2cfb4..d4efd046f7474 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -657,6 +657,11 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
return cgf.emitCXXNewExpr(e);
}
+ mlir::Value VisitCXXThrowExpr(const CXXThrowExpr *e) {
+ cgf.emitCXXThrowExpr(e);
+ return nullptr;
+ }
+
/// Emit a conversion from the specified type to the specified destination
/// type, both of which are CIR scalar types.
/// TODO: do we need ScalarConversionOpts here? Should be done in another
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 0ef8e7b7fbcac..af08bd465b2d2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1140,6 +1140,8 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *expr);
+ void emitCXXThrowExpr(const CXXThrowExpr *e);
+
void emitCtorPrologue(const clang::CXXConstructorDecl *ctor,
clang::CXXCtorType ctorType, FunctionArgList &args);
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 347656b5f6488..283376188267f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -56,6 +56,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
bool delegating, Address thisAddr,
QualType thisTy) override;
+ void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
+
bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
CXXDtorType dt) const override {
// Itanium does not emit any destructor variant as an inline thunk.
@@ -123,7 +125,7 @@ void CIRGenItaniumCXXABI::emitInstanceFunctionProlog(SourceLocation loc,
// Find out how to cirgen the complete destructor and constructor
namespace {
enum class StructorCIRGen { Emit, RAUW, Alias, COMDAT };
-}
+} // namespace
static StructorCIRGen getCIRGenToUse(CIRGenModule &cgm,
const CXXMethodDecl *md) {
@@ -289,6 +291,52 @@ void CIRGenItaniumCXXABI::emitDestructorCall(
vttTy, nullptr);
}
+// The idea here is creating a separate block for the throw with an
+// `UnreachableOp` as the terminator. So, we branch from the current block
+// to the throw block and create a block for the remaining operations.
+static void insertThrowAndSplit(mlir::OpBuilder &builder, mlir::Location loc,
+ mlir::Value exceptionPtr = {},
+ mlir::FlatSymbolRefAttr typeInfo = {},
+ mlir::FlatSymbolRefAttr dtor = {}) {
+ mlir::Block *currentBlock = builder.getInsertionBlock();
+ mlir::Region *region = currentBlock->getParent();
+
+ if (currentBlock->empty()) {
+ cir::ThrowOp::create(builder, loc, exceptionPtr, typeInfo, dtor);
+ cir::UnreachableOp::create(builder, loc);
+ } else {
+ mlir::Block *throwBlock = builder.createBlock(region);
+
+ cir::ThrowOp::create(builder, loc, exceptionPtr, typeInfo, dtor);
+ cir::UnreachableOp::create(builder, loc);
+
+ builder.setInsertionPointToEnd(currentBlock);
+ cir::BrOp::create(builder, loc, throwBlock);
+ }
+
+ (void)builder.createBlock(region);
+
+ // This will be erased during codegen, it acts as a placeholder for the
+ // operations to be inserted (if any)
+ cir::ScopeOp::create(builder, loc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ b.create<cir::YieldOp>(loc);
+ });
+}
+
+void CIRGenItaniumCXXABI::emitRethrow(CIRGenFunction &cgf, bool isNoReturn) {
+ // void __cxa_rethrow();
+
+ if (isNoReturn) {
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ assert(cgf.currSrcLoc && "expected source location");
+ mlir::Location loc = *cgf.currSrcLoc;
+ insertThrowAndSplit(builder, loc);
+ } else {
+ cgm.errorNYI("emitRethrow with isNoReturn false");
+ }
+}
+
CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
switch (cgm.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 7366446a33c6e..6d7072ad18696 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -20,6 +20,7 @@ add_clang_library(clangCIR
CIRGenBuiltin.cpp
CIRGenDecl.cpp
CIRGenDeclOpenACC.cpp
+ CIRGenException.cpp
CIRGenExpr.cpp
CIRGenExprAggregate.cpp
CIRGenExprComplex.cpp
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 167b970cdda12..ea39db64622bc 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2646,6 +2646,24 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser,
return mlir::success();
}
+//===----------------------------------------------------------------------===//
+// ThrowOp
+//===----------------------------------------------------------------------===//
+
+mlir::LogicalResult cir::ThrowOp::verify() {
+ // For the no-rethrow version, it must have at least the exception pointer.
+ if (rethrows())
+ return success();
+
+ if (getNumOperands() == 1) {
+ if (getTypeInfo())
+ return success();
+ return emitOpError() << "'type_info' symbol attribute missing";
+ }
+
+ return failure();
+}
+
//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 66260eb36e002..8f192eae3926b 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -8,7 +8,6 @@
#include "PassDetail.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/CharUnits.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
@@ -31,6 +30,7 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void lowerUnaryOp(cir::UnaryOp op);
void lowerArrayDtor(cir::ArrayDtor op);
void lowerArrayCtor(cir::ArrayCtor op);
+ void lowerThrowOp(ThrowOp op);
cir::FuncOp buildRuntimeFunction(
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
@@ -405,17 +405,37 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
true);
}
+void LoweringPreparePass::lowerThrowOp(ThrowOp op) {
+ if (op.rethrows()) {
+ CIRBaseBuilderTy builder(getContext());
+ auto voidTy = cir::VoidType::get(builder.getContext());
+ auto fnType = cir::FuncType::get({}, voidTy);
+
+ builder.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
+ FuncOp f =
+ buildRuntimeFunction(builder, "__cxa_rethrow", op.getLoc(), fnType);
+
+ builder.setInsertionPointAfter(op.getOperation());
+ cir::CallOp call = builder.createTryCallOp(op.getLoc(), f, {});
+
+ op->replaceAllUsesWith(call);
+ op->erase();
+ }
+}
+
void LoweringPreparePass::runOnOp(mlir::Operation *op) {
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
lowerArrayCtor(arrayCtor);
else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
lowerArrayDtor(arrayDtor);
- else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
- lowerCastOp(cast);
- else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op))
- lowerComplexMulOp(complexMul);
- else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
- lowerUnaryOp(unary);
+ else if (auto castOp = mlir::dyn_cast<cir::CastOp>(op))
+ lowerCastOp(castOp);
+ else if (auto complexMulOp = mlir::dyn_cast<cir::ComplexMulOp>(op))
+ lowerComplexMulOp(complexMulOp);
+ else if (auto unaryOp = mlir::dyn_cast<cir::UnaryOp>(op))
+ lowerUnaryOp(unaryOp);
+ else if (auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op))
+ lowerThrowOp(throwOp);
}
void LoweringPreparePass::runOnOperation() {
@@ -427,7 +447,7 @@ void LoweringPreparePass::runOnOperation() {
op->walk([&](mlir::Operation *op) {
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
- cir::ComplexMulOp, cir::UnaryOp>(op))
+ cir::ComplexMulOp, cir::UnaryOp, cir::ThrowOp>(op))
opsToTransform.push_back(op);
});
diff --git a/clang/test/CIR/CodeGen/exceptions.cpp b/clang/test/CIR/CodeGen/exceptions.cpp
new file mode 100644
index 0000000000000..a1a87bcb2f393
--- /dev/null
+++ b/clang/test/CIR/CodeGen/exceptions.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void foo() {
+ throw;
+}
+
+// CIR: cir.call @__cxa_rethrow() : () -> ()
+// CIR: cir.unreachable
+
+// LLVM: call void @__cxa_rethrow()
+// LLVM: unreachable
+
+// OGCG: call void @__cxa_rethrow()
+// OGCG: unreachable
``````````
</details>
https://github.com/llvm/llvm-project/pull/154994
More information about the cfe-commits
mailing list