https://github.com/AmrDeveloper created https://github.com/llvm/llvm-project/pull/154994
This change adds support for the throw op without sub expression and with noreturn Issue #154992 >From ee45be1718bf21b394a8bb0893efff1a3416853c Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Fri, 22 Aug 2025 15:34:47 +0200 Subject: [PATCH] [CIR] Upstream Re-Throw with no return --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 21 +++++++- clang/include/clang/CIR/Dialect/IR/CIROps.td | 54 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 4 +- clang/lib/CIR/CodeGen/CIRGenException.cpp | 41 ++++++++++++++ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 5 ++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 + clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 50 ++++++++++++++++- clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 18 +++++++ .../Dialect/Transforms/LoweringPrepare.cpp | 36 ++++++++++--- clang/test/CIR/CodeGen/exceptions.cpp | 19 +++++++ 11 files changed, 240 insertions(+), 11 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenException.cpp create mode 100644 clang/test/CIR/CodeGen/exceptions.cpp 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits