Skip to content

Commit ddef9ad

Browse files
authored
[CIR] Upstream Exception ThrowOp with subexpr (#161818)
Upstream the support for ThrowOp with subexpr Issue #154992
1 parent 2499fe1 commit ddef9ad

File tree

9 files changed

+232
-16
lines changed

9 files changed

+232
-16
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4168,6 +4168,40 @@ def CIR_ThrowOp : CIR_Op<"throw"> {
41684168
let hasVerifier = 1;
41694169
}
41704170

4171+
//===----------------------------------------------------------------------===//
4172+
// AllocExceptionOp
4173+
//===----------------------------------------------------------------------===//
4174+
4175+
def CIR_AllocExceptionOp : CIR_Op<"alloc.exception"> {
4176+
let summary = "Allocates an exception according to Itanium ABI";
4177+
let description = [{
4178+
Implements a slightly higher level __cxa_allocate_exception:
4179+
4180+
`void *__cxa_allocate_exception(size_t thrown_size);`
4181+
4182+
If the operation fails, the program terminates rather than throw.
4183+
4184+
Example:
4185+
4186+
```mlir
4187+
// if (b == 0) {
4188+
// ...
4189+
// throw "...";
4190+
cir.if %10 {
4191+
%11 = cir.alloc_exception 8 -> !cir.ptr<!void>
4192+
... // store exception content into %11
4193+
cir.throw %11 : !cir.ptr<!cir.ptr<!u8i>>, ...
4194+
```
4195+
}];
4196+
4197+
let arguments = (ins I64Attr:$size);
4198+
let results = (outs Res<CIR_PointerType, "", [MemAlloc<DefaultResource>]>:$addr);
4199+
4200+
let assemblyFormat = [{
4201+
$size `->` qualified(type($addr)) attr-dict
4202+
}];
4203+
}
4204+
41714205
//===----------------------------------------------------------------------===//
41724206
// Atomic operations
41734207
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class CIRGenCXXABI {
113113
CIRGenFunction &cgf) = 0;
114114

115115
virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0;
116+
virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0;
116117

117118
virtual mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
118119
QualType ty) = 0;

clang/lib/CIR/CodeGen/CIRGenCleanup.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class alignas(EHScopeStack::ScopeStackAlignment) EHCleanupScope
104104
bool isNormalCleanup() const { return cleanupBits.isNormalCleanup; }
105105

106106
bool isActive() const { return cleanupBits.isActive; }
107+
void setActive(bool isActive) { cleanupBits.isActive = isActive; }
107108

108109
size_t getCleanupSize() const { return cleanupBits.cleanupSize; }
109110
void *getCleanupBuffer() { return this + 1; }
@@ -138,5 +139,13 @@ inline EHScopeStack::iterator EHScopeStack::begin() const {
138139
return iterator(startOfData);
139140
}
140141

142+
inline EHScopeStack::iterator
143+
EHScopeStack::find(stable_iterator savePoint) const {
144+
assert(savePoint.isValid() && "finding invalid savepoint");
145+
assert(savePoint.size <= stable_begin().size &&
146+
"finding savepoint after pop");
147+
return iterator(endOfBuffer - savePoint.size);
148+
}
149+
141150
} // namespace clang::CIRGen
142151
#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCLEANUP_H

clang/lib/CIR/CodeGen/CIRGenException.cpp

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,36 @@ void CIRGenFunction::emitCXXThrowExpr(const CXXThrowExpr *e) {
3131
if (throwType->isObjCObjectPointerType()) {
3232
cgm.errorNYI("emitCXXThrowExpr ObjCObjectPointerType");
3333
return;
34-
} else {
35-
cgm.errorNYI("emitCXXThrowExpr with subExpr");
36-
return;
3734
}
38-
} else {
39-
cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
35+
36+
cgm.getCXXABI().emitThrow(*this, e);
37+
return;
4038
}
39+
40+
cgm.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
41+
}
42+
43+
void CIRGenFunction::emitAnyExprToExn(const Expr *e, Address addr) {
44+
// Make sure the exception object is cleaned up if there's an
45+
// exception during initialization.
46+
assert(!cir::MissingFeatures::ehCleanupScope());
47+
48+
// __cxa_allocate_exception returns a void*; we need to cast this
49+
// to the appropriate type for the object.
50+
mlir::Type ty = convertTypeForMem(e->getType());
51+
Address typedAddr = addr.withElementType(builder, ty);
52+
53+
// From LLVM's codegen:
54+
// FIXME: this isn't quite right! If there's a final unelided call
55+
// to a copy constructor, then according to [except.terminate]p1 we
56+
// must call std::terminate() if that constructor throws, because
57+
// technically that copy occurs after the exception expression is
58+
// evaluated but before the exception is caught. But the best way
59+
// to handle that is to teach EmitAggExpr to do the final copy
60+
// differently if it can't be elided.
61+
emitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(),
62+
/*isInitializer=*/true);
63+
64+
// Deactivate the cleanup block.
65+
assert(!cir::MissingFeatures::ehCleanupScope());
4166
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,8 @@ class CIRGenFunction : public CIRGenTypeCache {
10901090
/// even if no aggregate location is provided.
10911091
RValue emitAnyExprToTemp(const clang::Expr *e);
10921092

1093+
void emitAnyExprToExn(const Expr *e, Address addr);
1094+
10931095
void emitArrayDestroy(mlir::Value begin, mlir::Value numElements,
10941096
QualType elementType, CharUnits elementAlign,
10951097
Destroyer *destroyer);

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
7070
QualType thisTy) override;
7171

7272
void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
73+
void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
7374

7475
bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
7576
CXXDtorType dt) const override {
@@ -1544,6 +1545,59 @@ void CIRGenItaniumCXXABI::emitRethrow(CIRGenFunction &cgf, bool isNoReturn) {
15441545
}
15451546
}
15461547

1548+
void CIRGenItaniumCXXABI::emitThrow(CIRGenFunction &cgf,
1549+
const CXXThrowExpr *e) {
1550+
// This differs a bit from LLVM codegen, CIR has native operations for some
1551+
// cxa functions, and defers allocation size computation, always pass the dtor
1552+
// symbol, etc. CIRGen also does not use getAllocateExceptionFn / getThrowFn.
1553+
1554+
// Now allocate the exception object.
1555+
CIRGenBuilderTy &builder = cgf.getBuilder();
1556+
QualType clangThrowType = e->getSubExpr()->getType();
1557+
cir::PointerType throwTy =
1558+
builder.getPointerTo(cgf.convertType(clangThrowType));
1559+
uint64_t typeSize =
1560+
cgf.getContext().getTypeSizeInChars(clangThrowType).getQuantity();
1561+
mlir::Location subExprLoc = cgf.getLoc(e->getSubExpr()->getSourceRange());
1562+
1563+
// Defer computing allocation size to some later lowering pass.
1564+
mlir::TypedValue<cir::PointerType> exceptionPtr =
1565+
cir::AllocExceptionOp::create(builder, subExprLoc, throwTy,
1566+
builder.getI64IntegerAttr(typeSize))
1567+
.getAddr();
1568+
1569+
// Build expression and store its result into exceptionPtr.
1570+
CharUnits exnAlign = cgf.getContext().getExnObjectAlignment();
1571+
cgf.emitAnyExprToExn(e->getSubExpr(), Address(exceptionPtr, exnAlign));
1572+
1573+
// Get the RTTI symbol address.
1574+
auto typeInfo = mlir::cast<cir::GlobalViewAttr>(
1575+
cgm.getAddrOfRTTIDescriptor(subExprLoc, clangThrowType,
1576+
/*forEH=*/true));
1577+
assert(!typeInfo.getIndices() && "expected no indirection");
1578+
1579+
// The address of the destructor.
1580+
//
1581+
// Note: LLVM codegen already optimizes out the dtor if the
1582+
// type is a record with trivial dtor (by passing down a
1583+
// null dtor). In CIR, we forward this info and allow for
1584+
// Lowering pass to skip passing the trivial function.
1585+
//
1586+
if (const RecordType *recordTy = clangThrowType->getAs<RecordType>()) {
1587+
CXXRecordDecl *rec =
1588+
cast<CXXRecordDecl>(recordTy->getOriginalDecl()->getDefinition());
1589+
assert(!cir::MissingFeatures::isTrivialCtorOrDtor());
1590+
if (!rec->hasTrivialDestructor()) {
1591+
cgm.errorNYI("emitThrow: non-trivial destructor");
1592+
return;
1593+
}
1594+
}
1595+
1596+
// Now throw the exception.
1597+
mlir::Location loc = cgf.getLoc(e->getSourceRange());
1598+
insertThrowAndSplit(builder, loc, exceptionPtr, typeInfo.getSymbol());
1599+
}
1600+
15471601
CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
15481602
switch (cgm.getASTContext().getCXXABIKind()) {
15491603
case TargetCXXABI::GenericItanium:

clang/lib/CIR/CodeGen/EHScopeStack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ class EHScopeStack {
175175
return stable_iterator(endOfBuffer - startOfData);
176176
}
177177

178+
/// Turn a stable reference to a scope depth into a unstable pointer
179+
/// to the EH stack.
180+
iterator find(stable_iterator savePoint) const;
181+
178182
/// Create a stable reference to the bottom of the EH stack.
179183
static stable_iterator stable_end() { return stable_iterator(0); }
180184
};

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2581,22 +2581,69 @@ void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter,
25812581
mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
25822582
cir::ThrowOp op, OpAdaptor adaptor,
25832583
mlir::ConversionPatternRewriter &rewriter) const {
2584-
if (op.rethrows()) {
2585-
auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
2586-
auto funcTy =
2587-
mlir::LLVM::LLVMFunctionType::get(getContext(), voidTy, {}, false);
2584+
mlir::Location loc = op.getLoc();
2585+
auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
25882586

2589-
auto mlirModule = op->getParentOfType<mlir::ModuleOp>();
2590-
rewriter.setInsertionPointToStart(&mlirModule.getBodyRegion().front());
2587+
if (op.rethrows()) {
2588+
auto funcTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {});
25912589

2590+
// Get or create `declare void @__cxa_rethrow()`
25922591
const llvm::StringRef functionName = "__cxa_rethrow";
25932592
createLLVMFuncOpIfNotExist(rewriter, op, functionName, funcTy);
25942593

2595-
rewriter.setInsertionPointAfter(op.getOperation());
2596-
rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2597-
op, mlir::TypeRange{}, functionName, mlir::ValueRange{});
2594+
auto cxaRethrow = mlir::LLVM::CallOp::create(
2595+
rewriter, loc, mlir::TypeRange{}, functionName);
2596+
2597+
rewriter.replaceOp(op, cxaRethrow);
2598+
return mlir::success();
25982599
}
25992600

2601+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
2602+
auto fnTy = mlir::LLVM::LLVMFunctionType::get(
2603+
voidTy, {llvmPtrTy, llvmPtrTy, llvmPtrTy});
2604+
2605+
// Get or create `declare void @__cxa_throw(ptr, ptr, ptr)`
2606+
const llvm::StringRef fnName = "__cxa_throw";
2607+
createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
2608+
2609+
mlir::Value typeInfo = mlir::LLVM::AddressOfOp::create(
2610+
rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
2611+
adaptor.getTypeInfoAttr());
2612+
2613+
mlir::Value dtor;
2614+
if (op.getDtor()) {
2615+
dtor = mlir::LLVM::AddressOfOp::create(rewriter, loc, llvmPtrTy,
2616+
adaptor.getDtorAttr());
2617+
} else {
2618+
dtor = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
2619+
}
2620+
2621+
auto cxaThrowCall = mlir::LLVM::CallOp::create(
2622+
rewriter, loc, mlir::TypeRange{}, fnName,
2623+
mlir::ValueRange{adaptor.getExceptionPtr(), typeInfo, dtor});
2624+
2625+
rewriter.replaceOp(op, cxaThrowCall);
2626+
return mlir::success();
2627+
}
2628+
2629+
mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
2630+
cir::AllocExceptionOp op, OpAdaptor adaptor,
2631+
mlir::ConversionPatternRewriter &rewriter) const {
2632+
// Get or create `declare ptr @__cxa_allocate_exception(i64)`
2633+
StringRef fnName = "__cxa_allocate_exception";
2634+
auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
2635+
auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
2636+
auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
2637+
2638+
createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
2639+
auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
2640+
adaptor.getSizeAttr());
2641+
2642+
auto allocaExceptionCall = mlir::LLVM::CallOp::create(
2643+
rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
2644+
mlir::ValueRange{exceptionSize});
2645+
2646+
rewriter.replaceOp(op, allocaExceptionCall);
26002647
return mlir::success();
26012648
}
26022649

clang/test/CIR/CodeGen/throws.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm %s -o %t.ll
66
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
77

8-
void foo() {
8+
void rethrow() {
99
throw;
1010
}
1111

@@ -18,7 +18,7 @@ void foo() {
1818
// OGCG: call void @__cxa_rethrow()
1919
// OGCG: unreachable
2020

21-
int foo1(int a, int b) {
21+
int rethrow_from_block(int a, int b) {
2222
if (b == 0)
2323
throw;
2424
return a / b;
@@ -83,3 +83,43 @@ int foo1(int a, int b) {
8383
// OGCG: %[[TMP_B:.*]] = load i32, ptr %[[B_ADDR]], align 4
8484
// OGCG: %[[DIV_A_B:.*]] = sdiv i32 %[[TMP_A]], %[[TMP_B]]
8585
// OGCG: ret i32 %[[DIV_A_B]]
86+
87+
void throw_scalar() {
88+
throw 1;
89+
}
90+
91+
// CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
92+
// CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<1> : !s32i
93+
// CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i>
94+
// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
95+
// CIR: cir.unreachable
96+
97+
// LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
98+
// LLVM: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16
99+
// LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
100+
// LLVM: unreachable
101+
102+
// OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
103+
// OGCG: store i32 1, ptr %[[EXCEPTION_ADDR]], align 16
104+
// OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
105+
// OGCG: unreachable
106+
107+
void paren_expr() { (throw 0, 1 + 2); }
108+
109+
// CIR: %[[EXCEPTION_ADDR:.*]] = cir.alloc.exception 4 -> !cir.ptr<!s32i>
110+
// CIR: %[[EXCEPTION_VALUE:.*]] = cir.const #cir.int<0> : !s32i
111+
// CIR: cir.store{{.*}} %[[EXCEPTION_VALUE]], %[[EXCEPTION_ADDR]] : !s32i, !cir.ptr<!s32i>
112+
// CIR: cir.throw %[[EXCEPTION_ADDR]] : !cir.ptr<!s32i>, @_ZTIi
113+
// CIR: cir.unreachable
114+
// CIR: ^bb1:
115+
// CIR: %[[CONST_1:.*]] = cir.const #cir.int<1> : !s32i
116+
// CIR: %[[CONST_2:.*]] = cir.const #cir.int<2> : !s32i
117+
// CIR: %[[ADD:.*]] = cir.binop(add, %[[CONST_1]], %[[CONST_2]]) nsw : !s32i
118+
119+
// LLVM: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
120+
// LLVM: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16
121+
// LLVM: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)
122+
123+
// OGCG: %[[EXCEPTION_ADDR:.*]] = call ptr @__cxa_allocate_exception(i64 4)
124+
// OGCG: store i32 0, ptr %[[EXCEPTION_ADDR]], align 16
125+
// OGCG: call void @__cxa_throw(ptr %[[EXCEPTION_ADDR]], ptr @_ZTIi, ptr null)

0 commit comments

Comments
 (0)