[CIR] Implement EH lowering to Itanium form and LLVM IR (#184386)
This introduces a new pass to lower from a flattened, target-independent form of CIR to a form that uses Itanium-specific representation for exception handling. It also includes a small amount of code needed to lower the Itanium form to LLVM IR. Substantial amounts of this PR were created using agentic AI tools, but I have carefully reviewed the code, comments, and tests and made changes as needed.
This commit is contained in:
parent
5586d93a87
commit
375d65ee8d
@ -3486,7 +3486,6 @@ def CIR_TryCallOp : CIR_CallOpBase<"try_call",[
|
||||
);
|
||||
|
||||
let skipDefaultBuilders = 1;
|
||||
let hasLLVMLowering = false;
|
||||
|
||||
let builders = [
|
||||
OpBuilder<(ins "mlir::SymbolRefAttr":$callee,
|
||||
|
||||
@ -24,6 +24,7 @@ namespace mlir {
|
||||
std::unique_ptr<Pass> createCIRCanonicalizePass();
|
||||
std::unique_ptr<Pass> createCIRFlattenCFGPass();
|
||||
std::unique_ptr<Pass> createCIRSimplifyPass();
|
||||
std::unique_ptr<Pass> createCIREHABILoweringPass();
|
||||
std::unique_ptr<Pass> createCXXABILoweringPass();
|
||||
std::unique_ptr<Pass> createTargetLoweringPass();
|
||||
std::unique_ptr<Pass> createHoistAllocasPass();
|
||||
|
||||
@ -150,6 +150,29 @@ def TargetLowering : Pass<"cir-target-lowering", "mlir::ModuleOp"> {
|
||||
let dependentDialects = ["cir::CIRDialect"];
|
||||
}
|
||||
|
||||
def CIREHABILowering : Pass<"cir-eh-abi-lowering", "mlir::ModuleOp"> {
|
||||
let summary = "Lower flattened CIR EH operations to target-specific ABI form";
|
||||
let description = [{
|
||||
This pass lowers the ABI-agnostic exception handling operations produced by
|
||||
CFG flattening into an ABI-specific form. Currently only the Itanium C++
|
||||
ABI is implemented.
|
||||
|
||||
For the Itanium ABI, the pass performs the following transformations:
|
||||
- Replaces `cir.eh.initiate` with `cir.eh.inflight_exception`
|
||||
- Replaces `cir.eh.dispatch` with `cir.eh.typeid` + comparison chains
|
||||
- Removes `cir.begin_cleanup` and `cir.end_cleanup` operations
|
||||
- Replaces `cir.begin_catch` with a call to `__cxa_begin_catch`
|
||||
- Replaces `cir.end_catch` with a call to `__cxa_end_catch`
|
||||
- Replaces `cir.resume` with `cir.resume.flat`
|
||||
- Sets the personality function attribute on functions that require EH
|
||||
|
||||
If a non-Itanium ABI is specified, the pass emits a diagnostic indicating
|
||||
that the target is not yet implemented.
|
||||
}];
|
||||
let constructor = "mlir::createCIREHABILoweringPass()";
|
||||
let dependentDialects = ["cir::CIRDialect"];
|
||||
}
|
||||
|
||||
def LoweringPrepare : Pass<"cir-lowering-prepare"> {
|
||||
let summary = "Lower to more fine-grained CIR operations before lowering to "
|
||||
"other dialects";
|
||||
|
||||
@ -114,8 +114,6 @@ struct MissingFeatures {
|
||||
static bool opCallExtParameterInfo() { return false; }
|
||||
static bool opCallCIRGenFuncInfoParamInfo() { return false; }
|
||||
static bool opCallCIRGenFuncInfoExtParamInfo() { return false; }
|
||||
static bool opCallLandingPad() { return false; }
|
||||
static bool opCallContinueBlock() { return false; }
|
||||
static bool opCallChain() { return false; }
|
||||
static bool opCallExceptionAttr() { return false; }
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ add_clang_library(MLIRCIRTransforms
|
||||
CIRCanonicalize.cpp
|
||||
CIRSimplify.cpp
|
||||
CXXABILowering.cpp
|
||||
EHABILowering.cpp
|
||||
TargetLowering.cpp
|
||||
FlattenCFG.cpp
|
||||
HoistAllocas.cpp
|
||||
|
||||
502
clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
Normal file
502
clang/lib/CIR/Dialect/Transforms/EHABILowering.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
//===- EHABILowering.cpp - Lower flattened CIR EH ops to ABI-specific form ===//
|
||||
//
|
||||
// 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 file implements a pass that lowers ABI-agnostic flattened CIR exception
|
||||
// handling operations into an ABI-specific form. Currently only the Itanium
|
||||
// C++ ABI is supported.
|
||||
//
|
||||
// The Itanium ABI lowering performs these transformations:
|
||||
// - cir.eh.initiate → cir.eh.inflight_exception (landing pad)
|
||||
// - cir.eh.dispatch → cir.eh.typeid + cir.cmp + cir.brcond chains
|
||||
// - cir.begin_cleanup → (removed)
|
||||
// - cir.end_cleanup → (removed)
|
||||
// - cir.begin_catch → call to __cxa_begin_catch
|
||||
// - cir.end_catch → call to __cxa_end_catch
|
||||
// - cir.resume → cir.resume.flat
|
||||
// - !cir.eh_token values → (!cir.ptr<!void>, !u32i) value pairs
|
||||
// - personality function set on functions requiring EH
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PassDetail.h"
|
||||
#include "mlir/IR/Builders.h"
|
||||
#include "clang/CIR/Dialect/IR/CIRDialect.h"
|
||||
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
|
||||
#include "clang/CIR/Dialect/IR/CIRTypes.h"
|
||||
#include "clang/CIR/Dialect/Passes.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
|
||||
using namespace mlir;
|
||||
using namespace cir;
|
||||
|
||||
namespace mlir {
|
||||
#define GEN_PASS_DEF_CIREHABILOWERING
|
||||
#include "clang/CIR/Dialect/Passes.h.inc"
|
||||
} // namespace mlir
|
||||
|
||||
namespace {
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Shared utilities
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Ensure a function with the given name and type exists in the module. If it
|
||||
/// does not exist, create a private external declaration.
|
||||
static cir::FuncOp getOrCreateRuntimeFuncDecl(mlir::ModuleOp mod,
|
||||
mlir::Location loc,
|
||||
StringRef name,
|
||||
cir::FuncType funcTy) {
|
||||
if (auto existing = mod.lookupSymbol<cir::FuncOp>(name))
|
||||
return existing;
|
||||
|
||||
mlir::OpBuilder builder(mod.getContext());
|
||||
builder.setInsertionPointToEnd(mod.getBody());
|
||||
auto funcOp = cir::FuncOp::create(builder, loc, name, funcTy);
|
||||
funcOp.setLinkage(cir::GlobalLinkageKind::ExternalLinkage);
|
||||
funcOp.setPrivate();
|
||||
return funcOp;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// EH ABI Lowering Base Class
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Abstract base class for exception-handling ABI lowering.
|
||||
/// Each supported ABI (Itanium, Microsoft, etc.) provides a concrete subclass.
|
||||
class EHABILowering {
|
||||
public:
|
||||
explicit EHABILowering(mlir::ModuleOp mod)
|
||||
: mod(mod), ctx(mod.getContext()), builder(ctx) {}
|
||||
virtual ~EHABILowering() = default;
|
||||
|
||||
/// Lower all EH operations in the module to an ABI-specific form.
|
||||
virtual mlir::LogicalResult run() = 0;
|
||||
|
||||
protected:
|
||||
mlir::ModuleOp mod;
|
||||
mlir::MLIRContext *ctx;
|
||||
mlir::OpBuilder builder;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Itanium EH ABI Lowering
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Lowers flattened CIR EH operations to the Itanium C++ ABI form.
|
||||
///
|
||||
/// The entry point is run(), which iterates over all functions and
|
||||
/// calls lowerFunc() for each. lowerFunc() drives all lowering from
|
||||
/// cir.eh.initiate operations: every other EH op (begin/end_cleanup,
|
||||
/// eh.dispatch, begin/end_catch, resume) is reachable by tracing the
|
||||
/// eh_token produced by the initiate through its users.
|
||||
class ItaniumEHLowering : public EHABILowering {
|
||||
public:
|
||||
using EHABILowering::EHABILowering;
|
||||
mlir::LogicalResult run() override;
|
||||
|
||||
private:
|
||||
/// Maps a !cir.eh_token value to its Itanium ABI replacement pair:
|
||||
/// an exception pointer (!cir.ptr<!void>) and a type id (!u32i).
|
||||
using EhTokenMap = DenseMap<mlir::Value, std::pair<mlir::Value, mlir::Value>>;
|
||||
|
||||
cir::VoidType voidType;
|
||||
cir::PointerType voidPtrType;
|
||||
cir::PointerType u8PtrType;
|
||||
cir::IntType u32Type;
|
||||
|
||||
// Cached runtime function declarations, initialized when needed by
|
||||
// ensureRuntimeDecls().
|
||||
cir::FuncOp personalityFunc;
|
||||
cir::FuncOp beginCatchFunc;
|
||||
cir::FuncOp endCatchFunc;
|
||||
|
||||
constexpr const static ::llvm::StringLiteral kGxxPersonality =
|
||||
"__gxx_personality_v0";
|
||||
|
||||
void ensureRuntimeDecls(mlir::Location loc);
|
||||
mlir::LogicalResult lowerFunc(cir::FuncOp funcOp);
|
||||
void lowerEhInitiate(cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
|
||||
SmallVectorImpl<mlir::Operation *> &deadOps);
|
||||
void lowerDispatch(cir::EhDispatchOp dispatch, mlir::Value exnPtr,
|
||||
mlir::Value typeId,
|
||||
SmallVectorImpl<mlir::Operation *> &deadOps);
|
||||
};
|
||||
|
||||
/// Lower all EH operations in the module to the Itanium-specific form.
|
||||
mlir::LogicalResult ItaniumEHLowering::run() {
|
||||
// Pre-compute the common types used throughout all function lowerings.
|
||||
// TODO(cir): Move these to the base class if they are also needed for MSVC.
|
||||
voidType = cir::VoidType::get(ctx);
|
||||
voidPtrType = cir::PointerType::get(voidType);
|
||||
auto u8Type = cir::IntType::get(ctx, 8, /*isSigned=*/false);
|
||||
u8PtrType = cir::PointerType::get(u8Type);
|
||||
u32Type = cir::IntType::get(ctx, 32, /*isSigned=*/false);
|
||||
|
||||
for (cir::FuncOp funcOp : mod.getOps<cir::FuncOp>()) {
|
||||
if (mlir::failed(lowerFunc(funcOp)))
|
||||
return mlir::failure();
|
||||
}
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
/// Ensure the necessary Itanium runtime function declarations exist in the
|
||||
/// module.
|
||||
void ItaniumEHLowering::ensureRuntimeDecls(mlir::Location loc) {
|
||||
// TODO(cir): Handle other personality functions. This probably isn't needed
|
||||
// here if we fix codegen to always set the personality function.
|
||||
if (!personalityFunc) {
|
||||
auto s32Type = cir::IntType::get(ctx, 32, /*isSigned=*/true);
|
||||
auto personalityFuncTy = cir::FuncType::get({}, s32Type, /*isVarArg=*/true);
|
||||
personalityFunc = getOrCreateRuntimeFuncDecl(mod, loc, kGxxPersonality,
|
||||
personalityFuncTy);
|
||||
}
|
||||
|
||||
if (!beginCatchFunc) {
|
||||
auto beginCatchFuncTy =
|
||||
cir::FuncType::get({voidPtrType}, u8PtrType, /*isVarArg=*/false);
|
||||
beginCatchFunc = getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_begin_catch",
|
||||
beginCatchFuncTy);
|
||||
}
|
||||
|
||||
if (!endCatchFunc) {
|
||||
auto endCatchFuncTy = cir::FuncType::get({}, voidType, /*isVarArg=*/false);
|
||||
endCatchFunc =
|
||||
getOrCreateRuntimeFuncDecl(mod, loc, "__cxa_end_catch", endCatchFuncTy);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower all EH operations in a single function.
|
||||
mlir::LogicalResult ItaniumEHLowering::lowerFunc(cir::FuncOp funcOp) {
|
||||
if (funcOp.isDeclaration())
|
||||
return mlir::success();
|
||||
|
||||
// All EH lowering follows from cir.eh.initiate operations. The token each
|
||||
// initiate produces connects it to every other EH op in the function
|
||||
// (begin/end_cleanup, eh.dispatch, begin/end_catch, resume) through the
|
||||
// token graph. A single walk to collect initiates is therefore sufficient.
|
||||
SmallVector<cir::EhInitiateOp> initiateOps;
|
||||
funcOp.walk([&](cir::EhInitiateOp op) { initiateOps.push_back(op); });
|
||||
if (initiateOps.empty())
|
||||
return mlir::success();
|
||||
|
||||
ensureRuntimeDecls(funcOp.getLoc());
|
||||
|
||||
// Set the personality function if it is not already set.
|
||||
// TODO(cir): The personality function should already have been set by this
|
||||
// point. If we've seen a try operation, it will have been set by
|
||||
// emitCXXTryStmt. If we only have cleanups, it may not have been set. We
|
||||
// need to fix that in CodeGen. This is a placeholder until that is done.
|
||||
if (!funcOp.getPersonality())
|
||||
funcOp.setPersonality(kGxxPersonality);
|
||||
|
||||
// Lower each initiate and all EH ops connected to it. The token map is
|
||||
// shared across all initiate operations. Multiple initiates may flow into the
|
||||
// same dispatch block, and the map ensures the arguments are registered
|
||||
// only once. Dispatch ops are scheduled for deferred removal so that sibling
|
||||
// initiates can still read catch types from a shared dispatch.
|
||||
EhTokenMap ehTokenMap;
|
||||
SmallVector<mlir::Operation *> deadOps;
|
||||
for (cir::EhInitiateOp initiateOp : initiateOps)
|
||||
lowerEhInitiate(initiateOp, ehTokenMap, deadOps);
|
||||
|
||||
// Erase operations that were deferred during per-initiate processing
|
||||
// (dispatch ops whose catch types were read by multiple initiates).
|
||||
for (mlir::Operation *op : deadOps)
|
||||
op->erase();
|
||||
|
||||
// Remove the !cir.eh_token block arguments that were replaced by (ptr, u32)
|
||||
// pairs. Iterate in reverse to preserve argument indices during removal.
|
||||
for (mlir::Block &block : funcOp.getBody()) {
|
||||
for (int i = block.getNumArguments() - 1; i >= 0; --i) {
|
||||
if (mlir::isa<cir::EhTokenType>(block.getArgument(i).getType()))
|
||||
block.eraseArgument(i);
|
||||
}
|
||||
}
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
|
||||
/// Lower all EH operations connected to a single cir.eh.initiate.
|
||||
///
|
||||
/// The cir.eh.initiate is the root of a token graph. The token it produces
|
||||
/// flows through branch edges to consuming operations:
|
||||
///
|
||||
/// cir.eh.initiate → (via cir.br) → cir.begin_cleanup
|
||||
/// → cir.end_cleanup (via cleanup_token)
|
||||
/// → (via cir.br) → cir.eh.dispatch
|
||||
/// → (successors) →
|
||||
/// cir.begin_catch
|
||||
/// → cir.end_catch
|
||||
/// (via catch_token)
|
||||
/// → cir.resume
|
||||
///
|
||||
/// A single traversal of the token graph discovers and processes every
|
||||
/// connected op inline. The inflight_exception is created up-front without
|
||||
/// a catch_type_list; when the dispatch is encountered during traversal,
|
||||
/// the catch types are read and set on the inflight op.
|
||||
///
|
||||
/// Dispatch ops are not erased during per-initiate processing because they may
|
||||
/// be used by other initiate ops that haven't yet been lowered. Instead they
|
||||
/// are added to \p deadOps and erased by the caller after all initiates have
|
||||
/// been lowered.
|
||||
///
|
||||
/// \p ehTokenMap is shared across all initiates in the function so that block
|
||||
/// arguments reachable from multiple sibling initiates are registered once.
|
||||
void ItaniumEHLowering::lowerEhInitiate(
|
||||
cir::EhInitiateOp initiateOp, EhTokenMap &ehTokenMap,
|
||||
SmallVectorImpl<mlir::Operation *> &deadOps) {
|
||||
mlir::Value rootToken = initiateOp.getEhToken();
|
||||
|
||||
// Create the inflight_exception without a catch_type_list. The catch types
|
||||
// will be set once we encounter the dispatch during the traversal below.
|
||||
builder.setInsertionPoint(initiateOp);
|
||||
auto inflightOp = cir::EhInflightOp::create(
|
||||
builder, initiateOp.getLoc(), /*cleanup=*/initiateOp.getCleanup(),
|
||||
/*catch_type_list=*/mlir::ArrayAttr{});
|
||||
|
||||
ehTokenMap[rootToken] = {inflightOp.getExceptionPtr(),
|
||||
inflightOp.getTypeId()};
|
||||
|
||||
// Single traversal of the token graph. For each token value (the root token
|
||||
// or a block argument that carries it), we snapshot its users, register
|
||||
// (ptr, u32) replacement arguments on successor blocks, then process every
|
||||
// user inline. This avoids collecting ops into separate vectors.
|
||||
SmallVector<mlir::Value> worklist;
|
||||
SmallPtrSet<mlir::Value, 8> visited;
|
||||
worklist.push_back(rootToken);
|
||||
|
||||
while (!worklist.empty()) {
|
||||
mlir::Value current = worklist.pop_back_val();
|
||||
if (!visited.insert(current).second)
|
||||
continue;
|
||||
|
||||
// Snapshot users before modifying any of them (erasing ops during
|
||||
// iteration would invalidate the use-list iterator).
|
||||
SmallVector<mlir::Operation *> users;
|
||||
for (mlir::OpOperand &use : current.getUses())
|
||||
users.push_back(use.getOwner());
|
||||
|
||||
// Register replacement block arguments on successor blocks (extending the
|
||||
// worklist), then lower the op itself.
|
||||
for (mlir::Operation *user : users) {
|
||||
// Trace into successor blocks to register (ptr, u32) replacement
|
||||
// arguments for any !cir.eh_token block arguments found there. Even
|
||||
// if a block arg was already registered by a sibling initiate, it is
|
||||
// still added to the worklist so that the traversal can reach the
|
||||
// shared dispatch to read catch types.
|
||||
for (unsigned s = 0; s < user->getNumSuccessors(); ++s) {
|
||||
mlir::Block *succ = user->getSuccessor(s);
|
||||
for (mlir::BlockArgument arg : succ->getArguments()) {
|
||||
if (!mlir::isa<cir::EhTokenType>(arg.getType()))
|
||||
continue;
|
||||
if (!ehTokenMap.count(arg)) {
|
||||
mlir::Value ptrArg = succ->addArgument(voidPtrType, arg.getLoc());
|
||||
mlir::Value u32Arg = succ->addArgument(u32Type, arg.getLoc());
|
||||
ehTokenMap[arg] = {ptrArg, u32Arg};
|
||||
}
|
||||
worklist.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto op = mlir::dyn_cast<cir::BeginCleanupOp>(user)) {
|
||||
// begin_cleanup / end_cleanup are no-ops for Itanium. Erase the
|
||||
// end_cleanup first (drops the cleanup_token use) then the begin.
|
||||
for (auto &tokenUsers :
|
||||
llvm::make_early_inc_range(op.getCleanupToken().getUses())) {
|
||||
if (auto endOp =
|
||||
mlir::dyn_cast<cir::EndCleanupOp>(tokenUsers.getOwner()))
|
||||
endOp.erase();
|
||||
}
|
||||
op.erase();
|
||||
} else if (auto op = mlir::dyn_cast<cir::BeginCatchOp>(user)) {
|
||||
// Replace end_catch → __cxa_end_catch (drops the catch_token use),
|
||||
// then replace begin_catch → __cxa_begin_catch.
|
||||
for (auto &tokenUsers :
|
||||
llvm::make_early_inc_range(op.getCatchToken().getUses())) {
|
||||
if (auto endOp =
|
||||
mlir::dyn_cast<cir::EndCatchOp>(tokenUsers.getOwner())) {
|
||||
builder.setInsertionPoint(endOp);
|
||||
cir::CallOp::create(builder, endOp.getLoc(),
|
||||
mlir::FlatSymbolRefAttr::get(endCatchFunc),
|
||||
voidType, mlir::ValueRange{});
|
||||
endOp.erase();
|
||||
}
|
||||
}
|
||||
|
||||
auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
|
||||
builder.setInsertionPoint(op);
|
||||
auto callOp = cir::CallOp::create(
|
||||
builder, op.getLoc(), mlir::FlatSymbolRefAttr::get(beginCatchFunc),
|
||||
u8PtrType, mlir::ValueRange{exnPtr});
|
||||
mlir::Value castResult = callOp.getResult();
|
||||
mlir::Type expectedPtrType = op.getExnPtr().getType();
|
||||
if (castResult.getType() != expectedPtrType)
|
||||
castResult =
|
||||
cir::CastOp::create(builder, op.getLoc(), expectedPtrType,
|
||||
cir::CastKind::bitcast, callOp.getResult());
|
||||
op.getExnPtr().replaceAllUsesWith(castResult);
|
||||
op.erase();
|
||||
} else if (auto op = mlir::dyn_cast<cir::EhDispatchOp>(user)) {
|
||||
// Read catch types from the dispatch and set them on the inflight op.
|
||||
mlir::ArrayAttr catchTypes = op.getCatchTypesAttr();
|
||||
if (catchTypes && catchTypes.size() > 0) {
|
||||
SmallVector<mlir::Attribute> typeSymbols;
|
||||
for (mlir::Attribute attr : catchTypes)
|
||||
typeSymbols.push_back(
|
||||
mlir::cast<cir::GlobalViewAttr>(attr).getSymbol());
|
||||
inflightOp.setCatchTypeListAttr(builder.getArrayAttr(typeSymbols));
|
||||
}
|
||||
// Only lower the dispatch once. A sibling initiate sharing the same
|
||||
// dispatch will still read its catch types (above), but the comparison
|
||||
// chain and branch replacement are only created the first time.
|
||||
if (!llvm::is_contained(deadOps, op.getOperation())) {
|
||||
auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
|
||||
lowerDispatch(op, exnPtr, typeId, deadOps);
|
||||
}
|
||||
} else if (auto op = mlir::dyn_cast<cir::ResumeOp>(user)) {
|
||||
auto [exnPtr, typeId] = ehTokenMap.lookup(op.getEhToken());
|
||||
builder.setInsertionPoint(op);
|
||||
cir::ResumeFlatOp::create(builder, op.getLoc(), exnPtr, typeId);
|
||||
op.erase();
|
||||
} else if (auto op = mlir::dyn_cast<cir::BrOp>(user)) {
|
||||
// Replace eh_token operands with the (ptr, u32) pair.
|
||||
SmallVector<mlir::Value> newOperands;
|
||||
bool changed = false;
|
||||
for (mlir::Value operand : op.getDestOperands()) {
|
||||
auto it = ehTokenMap.find(operand);
|
||||
if (it != ehTokenMap.end()) {
|
||||
newOperands.push_back(it->second.first);
|
||||
newOperands.push_back(it->second.second);
|
||||
changed = true;
|
||||
} else {
|
||||
newOperands.push_back(operand);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
builder.setInsertionPoint(op);
|
||||
cir::BrOp::create(builder, op.getLoc(), op.getDest(), newOperands);
|
||||
op.erase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initiateOp.erase();
|
||||
}
|
||||
|
||||
/// Lower a cir.eh.dispatch by creating a comparison chain in new blocks.
|
||||
/// The dispatch itself is replaced with a branch to the first comparison
|
||||
/// block and added to deadOps for deferred removal.
|
||||
void ItaniumEHLowering::lowerDispatch(
|
||||
cir::EhDispatchOp dispatch, mlir::Value exnPtr, mlir::Value typeId,
|
||||
SmallVectorImpl<mlir::Operation *> &deadOps) {
|
||||
mlir::Location dispLoc = dispatch.getLoc();
|
||||
mlir::Block *defaultDest = dispatch.getDefaultDestination();
|
||||
mlir::ArrayAttr catchTypes = dispatch.getCatchTypesAttr();
|
||||
mlir::SuccessorRange catchDests = dispatch.getCatchDestinations();
|
||||
mlir::Block *dispatchBlock = dispatch->getBlock();
|
||||
|
||||
// Build the comparison chain in new blocks inserted after the dispatch's
|
||||
// block. The dispatch itself is replaced with a branch to the first
|
||||
// comparison block and scheduled for deferred removal.
|
||||
if (!catchTypes || catchTypes.empty()) {
|
||||
// No typed catches: replace dispatch with a direct branch.
|
||||
builder.setInsertionPoint(dispatch);
|
||||
cir::BrOp::create(builder, dispLoc, defaultDest,
|
||||
mlir::ValueRange{exnPtr, typeId});
|
||||
} else {
|
||||
unsigned numCatches = catchTypes.size();
|
||||
|
||||
// Create and populate comparison blocks in reverse order so that each
|
||||
// block's false destination (the next comparison block, or defaultDest
|
||||
// for the last one) is already available. Each createBlock inserts
|
||||
// before the previous one, so the blocks end up in forward order.
|
||||
mlir::Block *insertBefore = dispatchBlock->getNextNode();
|
||||
mlir::Block *falseDest = defaultDest;
|
||||
mlir::Block *firstCmpBlock = nullptr;
|
||||
for (int i = numCatches - 1; i >= 0; --i) {
|
||||
auto *cmpBlock = builder.createBlock(insertBefore, {voidPtrType, u32Type},
|
||||
{dispLoc, dispLoc});
|
||||
|
||||
mlir::Value cmpExnPtr = cmpBlock->getArgument(0);
|
||||
mlir::Value cmpTypeId = cmpBlock->getArgument(1);
|
||||
|
||||
auto globalView = mlir::cast<cir::GlobalViewAttr>(catchTypes[i]);
|
||||
auto ehTypeIdOp =
|
||||
cir::EhTypeIdOp::create(builder, dispLoc, globalView.getSymbol());
|
||||
auto cmpOp = cir::CmpOp::create(builder, dispLoc, cir::CmpOpKind::eq,
|
||||
cmpTypeId, ehTypeIdOp.getTypeId());
|
||||
|
||||
cir::BrCondOp::create(builder, dispLoc, cmpOp, catchDests[i], falseDest,
|
||||
mlir::ValueRange{cmpExnPtr, cmpTypeId},
|
||||
mlir::ValueRange{cmpExnPtr, cmpTypeId});
|
||||
|
||||
insertBefore = cmpBlock;
|
||||
falseDest = cmpBlock;
|
||||
firstCmpBlock = cmpBlock;
|
||||
}
|
||||
|
||||
// Replace the dispatch with a branch to the first comparison block.
|
||||
builder.setInsertionPoint(dispatch);
|
||||
cir::BrOp::create(builder, dispLoc, firstCmpBlock,
|
||||
mlir::ValueRange{exnPtr, typeId});
|
||||
}
|
||||
|
||||
// Schedule the dispatch for deferred removal. We cannot erase it now because
|
||||
// a sibling initiate that shares this dispatch may still need to read its
|
||||
// catch types.
|
||||
deadOps.push_back(dispatch);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// The Pass
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct CIREHABILoweringPass
|
||||
: public impl::CIREHABILoweringBase<CIREHABILoweringPass> {
|
||||
CIREHABILoweringPass() = default;
|
||||
void runOnOperation() override;
|
||||
};
|
||||
|
||||
void CIREHABILoweringPass::runOnOperation() {
|
||||
auto mod = mlir::cast<mlir::ModuleOp>(getOperation());
|
||||
|
||||
// The target triple is attached to the module as the "cir.triple" attribute.
|
||||
// If it is absent (e.g. a CIR module parsed from text without a triple) we
|
||||
// cannot determine the ABI and must skip the pass.
|
||||
auto tripleAttr = mlir::dyn_cast_if_present<mlir::StringAttr>(
|
||||
mod->getAttr(cir::CIRDialect::getTripleAttrName()));
|
||||
if (!tripleAttr) {
|
||||
mod.emitError("Module has no target triple");
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the ABI-specific lowering handler from the triple. The Microsoft
|
||||
// C++ ABI targets a Windows MSVC environment; everything else uses Itanium.
|
||||
// Extend this when Microsoft ABI lowering is added.
|
||||
llvm::Triple triple(tripleAttr.getValue());
|
||||
std::unique_ptr<EHABILowering> lowering;
|
||||
if (triple.isWindowsMSVCEnvironment()) {
|
||||
mod.emitError(
|
||||
"EH ABI lowering is not yet implemented for the Microsoft ABI");
|
||||
return signalPassFailure();
|
||||
} else {
|
||||
lowering = std::make_unique<ItaniumEHLowering>(mod);
|
||||
}
|
||||
|
||||
if (mlir::failed(lowering->run()))
|
||||
return signalPassFailure();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Pass> mlir::createCIREHABILoweringPass() {
|
||||
return std::make_unique<CIREHABILoweringPass>();
|
||||
}
|
||||
@ -49,6 +49,7 @@ namespace mlir {
|
||||
void populateCIRPreLoweringPasses(OpPassManager &pm) {
|
||||
pm.addPass(createHoistAllocasPass());
|
||||
pm.addPass(createCIRFlattenCFGPass());
|
||||
pm.addPass(createCIREHABILoweringPass());
|
||||
pm.addPass(createGotoSolverPass());
|
||||
}
|
||||
|
||||
|
||||
@ -1837,7 +1837,9 @@ static mlir::LogicalResult
|
||||
rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
|
||||
mlir::ConversionPatternRewriter &rewriter,
|
||||
const mlir::TypeConverter *converter,
|
||||
mlir::FlatSymbolRefAttr calleeAttr) {
|
||||
mlir::FlatSymbolRefAttr calleeAttr,
|
||||
mlir::Block *continueBlock = nullptr,
|
||||
mlir::Block *landingPadBlock = nullptr) {
|
||||
llvm::SmallVector<mlir::Type, 8> llvmResults;
|
||||
mlir::ValueTypeRange<mlir::ResultRange> cirResults = op->getResultTypes();
|
||||
auto call = cast<cir::CIRCallOpInterface>(op);
|
||||
@ -1908,18 +1910,23 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
|
||||
converter->convertType(calleeFuncTy));
|
||||
}
|
||||
|
||||
assert(!cir::MissingFeatures::opCallLandingPad());
|
||||
assert(!cir::MissingFeatures::opCallContinueBlock());
|
||||
assert(!cir::MissingFeatures::opCallCallConv());
|
||||
|
||||
auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
|
||||
op, llvmFnTy, calleeAttr, callOperands);
|
||||
newOp->setAttrs(attributes);
|
||||
if (memoryEffects)
|
||||
newOp.setMemoryEffectsAttr(memoryEffects);
|
||||
newOp.setNoUnwind(noUnwind);
|
||||
newOp.setWillReturn(willReturn);
|
||||
newOp.setNoreturn(noReturn);
|
||||
if (landingPadBlock) {
|
||||
auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::InvokeOp>(
|
||||
op, llvmFnTy, calleeAttr, callOperands, continueBlock,
|
||||
mlir::ValueRange{}, landingPadBlock, mlir::ValueRange{});
|
||||
newOp->setAttrs(attributes);
|
||||
} else {
|
||||
auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
|
||||
op, llvmFnTy, calleeAttr, callOperands);
|
||||
newOp->setAttrs(attributes);
|
||||
if (memoryEffects)
|
||||
newOp.setMemoryEffectsAttr(memoryEffects);
|
||||
newOp.setNoUnwind(noUnwind);
|
||||
newOp.setWillReturn(willReturn);
|
||||
newOp.setNoreturn(noReturn);
|
||||
}
|
||||
|
||||
return mlir::success();
|
||||
}
|
||||
@ -1931,6 +1938,15 @@ mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
|
||||
getTypeConverter(), op.getCalleeAttr());
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRToLLVMTryCallOpLowering::matchAndRewrite(
|
||||
cir::TryCallOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
assert(!cir::MissingFeatures::opCallCallConv());
|
||||
return rewriteCallOrInvoke(op.getOperation(), adaptor.getOperands(), rewriter,
|
||||
getTypeConverter(), op.getCalleeAttr(),
|
||||
op.getNormalDest(), op.getUnwindDest());
|
||||
}
|
||||
|
||||
mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
|
||||
cir::ReturnAddrOp op, OpAdaptor adaptor,
|
||||
mlir::ConversionPatternRewriter &rewriter) const {
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
// 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
|
||||
|
||||
// TODO(cir): Merge this with try-catch.cpp.
|
||||
|
||||
int division();
|
||||
|
||||
void call_function_inside_try_catch_all() {
|
||||
@ -29,6 +33,43 @@ void call_function_inside_try_catch_all() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z34call_function_inside_try_catch_allv() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: catch ptr null
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: br label %[[BEGIN_CATCH:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI1]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z34call_function_inside_try_catch_allv() #0 personality ptr @__gxx_personality_v0
|
||||
// OGCG: %[[EXN_OBJ_ADDR:.*]] = alloca ptr, align 8
|
||||
@ -82,6 +123,58 @@ void call_function_inside_try_catch_with_exception_type() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z50call_function_inside_try_catch_with_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: catch ptr @_ZTIi
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: br label %[[DISPATCH:.*]]
|
||||
// LLVM: [[DISPATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
|
||||
// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
|
||||
// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: %[[LOAD:.*]] = load i32, ptr %[[TOKEN]], align 4
|
||||
// LLVM: store i32 %[[LOAD]], ptr {{.*}}, align 4
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[RESUME]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
|
||||
// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
|
||||
// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z50call_function_inside_try_catch_with_exception_typev() #0 personality ptr @__gxx_personality_v0
|
||||
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
|
||||
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
|
||||
@ -148,6 +241,58 @@ void call_function_inside_try_catch_with_complex_exception_type() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z58call_function_inside_try_catch_with_complex_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: catch ptr @_ZTICi
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: br label %[[DISPATCH:.*]]
|
||||
// LLVM: [[DISPATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTICi)
|
||||
// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
|
||||
// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: %[[LOAD:.*]] = load { i32, i32 }, ptr %[[TOKEN]], align 4
|
||||
// LLVM: store { i32, i32 } %[[LOAD]], ptr {{.*}}, align 4
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[RESUME]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
|
||||
// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
|
||||
// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z58call_function_inside_try_catch_with_complex_exception_typev() #0 personality ptr @__gxx_personality_v0
|
||||
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
|
||||
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
|
||||
@ -219,6 +364,57 @@ void call_function_inside_try_catch_with_array_exception_type() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z56call_function_inside_try_catch_with_array_exception_typev() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: catch ptr @_ZTIPi
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: br label %[[DISPATCH:.*]]
|
||||
// LLVM: [[DISPATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIPi)
|
||||
// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
|
||||
// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[RESUME:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: store ptr %[[TOKEN]], ptr {{.*}}, align 8
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[RESUME]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TMP_EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_OBJ_PHI3]], 0
|
||||
// LLVM: %[[EXCEPTION_INFO:.*]] = insertvalue { ptr, i32 } %[[TMP_EXCEPTION_INFO]], i32 %[[EH_SELECTOR_PHI3]], 1
|
||||
// LLVM: resume { ptr, i32 } %[[EXCEPTION_INFO]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z56call_function_inside_try_catch_with_array_exception_typev() #0 personality ptr @__gxx_personality_v0
|
||||
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
|
||||
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
|
||||
@ -292,6 +488,66 @@ void call_function_inside_try_catch_with_exception_type_and_catch_all() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z64call_function_inside_try_catch_with_exception_type_and_catch_allv() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: catch ptr @_ZTIi
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: br label %[[DISPATCH:.*]]
|
||||
// LLVM: [[DISPATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_TYPE_ID:.*]] = call i32 @llvm.eh.typeid.for.p0(ptr @_ZTIi)
|
||||
// LLVM: %[[TYPE_ID_EQ:.*]] = icmp eq i32 %[[EH_SELECTOR_PHI1]], %[[EH_TYPE_ID]]
|
||||
// LLVM: br i1 %[[TYPE_ID_EQ]], label %[[BEGIN_CATCH:.*]], label %[[CATCH_ALL:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: %[[LOAD:.*]] = load i32, ptr %[[TOKEN]], align 4
|
||||
// LLVM: store i32 %[[LOAD]], ptr {{.*}}, align 4
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[CATCH_ALL]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI3:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI3:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[DISPATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN2:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI3]])
|
||||
// LLVM: br label %[[CATCH_ALL_BODY:.*]]
|
||||
// LLVM: [[CATCH_ALL_BODY]]:
|
||||
// LLVM: br label %[[END_CATCH2:.*]]
|
||||
// LLVM: [[END_CATCH2]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH2:.*]]
|
||||
// LLVM: [[END_DISPATCH2]]:
|
||||
// LLVM: br label %[[END_TRY2:.*]]
|
||||
// LLVM: [[END_TRY2]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z64call_function_inside_try_catch_with_exception_type_and_catch_allv() #0 personality ptr @__gxx_personality_v0
|
||||
// OGCG: %[[EXCEPTION_ADDR:.*]] = alloca ptr, align 8
|
||||
// OGCG: %[[EH_TYPE_ID_ADDR:.*]] = alloca i32, align 4
|
||||
@ -364,6 +620,56 @@ void cleanup_inside_try_body() {
|
||||
// CIR: }
|
||||
// CIR: }
|
||||
|
||||
// LLVM: define {{.*}} void @_Z23cleanup_inside_try_bodyv() {{.*}} personality ptr @__gxx_personality_v0
|
||||
// LLVM: br label %[[TRY_SCOPE:.*]]
|
||||
// LLVM: [[TRY_SCOPE]]:
|
||||
// LLVM: br label %[[TRY_BEGIN:.*]]
|
||||
// LLVM: [[TRY_BEGIN]]:
|
||||
// LLVM: br label %[[CLEANUP_SCOPE:.*]]
|
||||
// LLVM: [[CLEANUP_SCOPE]]:
|
||||
// LLVM: %[[CALL:.*]] = invoke i32 @_Z8divisionv()
|
||||
// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
|
||||
// LLVM: [[INVOKE_CONT]]:
|
||||
// LLVM: br label %[[CLEANUP:.*]]
|
||||
// LLVM: [[CLEANUP]]:
|
||||
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}})
|
||||
// LLVM: br label %[[END_CLEANUP:.*]]
|
||||
// LLVM: [[END_CLEANUP]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[LANDING_PAD]]:
|
||||
// LLVM: %[[LP:.*]] = landingpad { ptr, i32 }
|
||||
// LLVM: cleanup
|
||||
// LLVM: %[[EXN_OBJ:.*]] = extractvalue { ptr, i32 } %[[LP]], 0
|
||||
// LLVM: %[[EH_SELECTOR_VAL:.*]] = extractvalue { ptr, i32 } %[[LP]], 1
|
||||
// LLVM: br label %[[CLEANUP_LANDING:.*]]
|
||||
// LLVM: [[CLEANUP_LANDING]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI:.*]] = phi ptr [ %[[EXN_OBJ:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI:.*]] = phi i32 [ %[[EH_SELECTOR_VAL:.*]], %[[LANDING_PAD:.*]] ]
|
||||
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}})
|
||||
// LLVM: br label %[[CATCH:.*]]
|
||||
// LLVM: [[CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI1:.*]] = phi ptr [ %[[EXN_OBJ_PHI:.*]], %[[CLEANUP_LANDING:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI1:.*]] = phi i32 [ %[[EH_SELECTOR_PHI:.*]], %[[CLEANUP_LANDING:.*]] ]
|
||||
// LLVM: br label %[[BEGIN_CATCH:.*]]
|
||||
// LLVM: [[BEGIN_CATCH]]:
|
||||
// LLVM: %[[EXN_OBJ_PHI2:.*]] = phi ptr [ %[[EXN_OBJ_PHI1:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[EH_SELECTOR_PHI2:.*]] = phi i32 [ %[[EH_SELECTOR_PHI1:.*]], %[[CATCH:.*]] ]
|
||||
// LLVM: %[[TOKEN:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXN_OBJ_PHI2]])
|
||||
// LLVM: br label %[[CATCH_BODY:.*]]
|
||||
// LLVM: [[CATCH_BODY]]:
|
||||
// LLVM: br label %[[END_CATCH:.*]]
|
||||
// LLVM: [[END_CATCH]]:
|
||||
// LLVM: call void @__cxa_end_catch()
|
||||
// LLVM: br label %[[END_DISPATCH:.*]]
|
||||
// LLVM: [[END_DISPATCH]]:
|
||||
// LLVM: br label %[[END_TRY:.*]]
|
||||
// LLVM: [[END_TRY]]:
|
||||
// LLVM: br label %[[TRY_CONT:.*]]
|
||||
// LLVM: [[TRY_CONT]]:
|
||||
// LLVM: br label %[[DONE:.*]]
|
||||
// LLVM: [[DONE]]:
|
||||
// LLVM: ret void
|
||||
|
||||
// OGCG: define {{.*}} void @_Z23cleanup_inside_try_bodyv() {{.*}} personality ptr @__gxx_personality_v0 {
|
||||
// OGCG: %[[S:.*]] = alloca %struct.S
|
||||
// OGCG: %[[EXN_SLOT:.*]] = alloca ptr
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// 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
|
||||
|
||||
// TODO(cir): Reenable lowering to LLVM after new try-catch lowering is implemented.
|
||||
|
||||
void empty_try_block_with_catch_all() {
|
||||
try {} catch (...) {}
|
||||
}
|
||||
|
||||
514
clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir
Normal file
514
clang/test/CIR/Transforms/eh-abi-lowering-itanium.cir
Normal file
@ -0,0 +1,514 @@
|
||||
// RUN: cir-opt %s -cir-eh-abi-lowering -o %t.cir
|
||||
// RUN: FileCheck --input-file=%t.cir %s
|
||||
|
||||
// Test Itanium ABI lowering of flattened CIR exception handling operations.
|
||||
// The input is the form produced by cir-flatten-cfg, and the output should
|
||||
// have all EH-specific tokens replaced with concrete values and ABI calls.
|
||||
|
||||
!s32i = !cir.int<s, 32>
|
||||
!u32i = !cir.int<u, 32>
|
||||
!u8i = !cir.int<u, 8>
|
||||
!void = !cir.void
|
||||
!rec_SomeClass = !cir.record<struct "SomeClass" {!s32i}>
|
||||
!rec_exception = !cir.record<struct "std::exception" {!s32i}>
|
||||
|
||||
module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
|
||||
|
||||
// Test: EH cleanup only (no catch handlers).
|
||||
// cir.eh.initiate cleanup → cir.eh.inflight_exception cleanup
|
||||
// cir.begin_cleanup / cir.end_cleanup → removed
|
||||
// cir.resume → cir.resume.flat
|
||||
cir.func @test_cleanup_only() {
|
||||
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
|
||||
cir.call @ctor(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.try_call @doSomething(%0) ^bb1, ^bb2 : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
^bb1:
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.br ^bb5
|
||||
^bb2:
|
||||
%1 = cir.eh.initiate cleanup : !cir.eh_token
|
||||
cir.br ^bb3(%1 : !cir.eh_token)
|
||||
^bb3(%eh_token : !cir.eh_token):
|
||||
%2 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.end_cleanup %2 : !cir.cleanup_token
|
||||
cir.resume %eh_token : !cir.eh_token
|
||||
^bb5:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_cleanup_only()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// Normal path unchanged.
|
||||
// CHECK: ^[[NORMAL]]:
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Unwind: eh.initiate replaced with eh.inflight_exception.
|
||||
// CHECK: ^[[UNWIND]]:
|
||||
// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception cleanup
|
||||
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Cleanup: begin_cleanup/end_cleanup removed, resume becomes resume.flat.
|
||||
// CHECK: ^[[CLEANUP]](%[[C_EXN:.*]]: !cir.ptr<!void>, %[[C_TID:.*]]: !u32i):
|
||||
// CHECK-NOT: cir.begin_cleanup
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK-NOT: cir.end_cleanup
|
||||
// CHECK: cir.resume.flat %[[C_EXN]], %[[C_TID]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: Try-catch with catch-all handler.
|
||||
// cir.eh.dispatch [catch_all] → cir.br to catch-all block
|
||||
// cir.begin_catch → __cxa_begin_catch + bitcast
|
||||
// cir.end_catch → __cxa_end_catch
|
||||
cir.func @test_catch_all() {
|
||||
cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
|
||||
^bb1:
|
||||
cir.br ^bb5
|
||||
^bb2:
|
||||
%1 = cir.eh.initiate : !cir.eh_token
|
||||
cir.br ^bb3(%1 : !cir.eh_token)
|
||||
^bb3(%eh_token : !cir.eh_token):
|
||||
cir.eh.dispatch %eh_token : !cir.eh_token [
|
||||
catch_all : ^bb4
|
||||
]
|
||||
^bb4(%eh_token.1 : !cir.eh_token):
|
||||
%ct, %exn = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
|
||||
cir.end_catch %ct : !cir.catch_token
|
||||
cir.br ^bb5
|
||||
^bb5:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_catch_all()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: ^[[NORMAL]]:
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Landing pad with catch_all (no cleanup, no typed catches).
|
||||
// CHECK: ^[[UNWIND]]:
|
||||
// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception
|
||||
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch replaced with direct branch to catch-all handler.
|
||||
// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
|
||||
// CHECK: cir.br ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Catch-all: begin_catch → __cxa_begin_catch, end_catch → __cxa_end_catch.
|
||||
// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: %{{.*}} = cir.call @__cxa_begin_catch(%[[CA_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: Try-catch with typed handler and unwind.
|
||||
// cir.eh.dispatch with typed catch → typeid + cmp + brcond chain
|
||||
// unwind handler → cir.resume.flat
|
||||
cir.func @test_typed_catch_with_unwind() {
|
||||
cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
|
||||
^bb1:
|
||||
cir.br ^bb6
|
||||
^bb2:
|
||||
%1 = cir.eh.initiate : !cir.eh_token
|
||||
cir.br ^bb3(%1 : !cir.eh_token)
|
||||
^bb3(%eh_token : !cir.eh_token):
|
||||
cir.eh.dispatch %eh_token : !cir.eh_token [
|
||||
catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb4,
|
||||
unwind : ^bb5
|
||||
]
|
||||
^bb4(%eh_token.1 : !cir.eh_token):
|
||||
%ct, %exn = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
|
||||
cir.end_catch %ct : !cir.catch_token
|
||||
cir.br ^bb6
|
||||
^bb5(%eh_token.2 : !cir.eh_token):
|
||||
cir.resume %eh_token.2 : !cir.eh_token
|
||||
^bb6:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_typed_catch_with_unwind()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: ^[[NORMAL]]:
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Landing pad with typed catch list.
|
||||
// CHECK: ^[[UNWIND]]:
|
||||
// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception [@_ZTISt9exception]
|
||||
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch: branch to comparison chain.
|
||||
// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
|
||||
//
|
||||
// Comparison: typeid comparison + brcond.
|
||||
// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
|
||||
// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
|
||||
// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
|
||||
// CHECK: cir.brcond %[[CMP]] ^[[CATCH:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[UNWIND_HANDLER:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Typed catch handler.
|
||||
// CHECK: ^[[CATCH]](%[[C_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: %{{.*}} = cir.call @__cxa_begin_catch(%[[C_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// Unwind handler: resume.flat.
|
||||
// CHECK: ^[[UNWIND_HANDLER]](%[[UW_EXN:.*]]: !cir.ptr<!void>, %[[UW_TID:.*]]: !u32i):
|
||||
// CHECK: cir.resume.flat %[[UW_EXN]], %[[UW_TID]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: Try-catch with multiple typed handlers and catch-all.
|
||||
// Dispatch → chain of typeid comparisons, catch-all as final default.
|
||||
cir.func @test_multiple_typed_and_catch_all() {
|
||||
cir.try_call @mayThrow() ^bb1, ^bb2 : () -> ()
|
||||
^bb1:
|
||||
cir.br ^bb7
|
||||
^bb2:
|
||||
%1 = cir.eh.initiate : !cir.eh_token
|
||||
cir.br ^bb3(%1 : !cir.eh_token)
|
||||
^bb3(%eh_token : !cir.eh_token):
|
||||
cir.eh.dispatch %eh_token : !cir.eh_token [
|
||||
catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb4,
|
||||
catch(#cir.global_view<@_ZTIi> : !cir.ptr<!u8i>) : ^bb5,
|
||||
catch_all : ^bb6
|
||||
]
|
||||
^bb4(%eh_token.1 : !cir.eh_token):
|
||||
%ct1, %exn1 = cir.begin_catch %eh_token.1 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
|
||||
cir.end_catch %ct1 : !cir.catch_token
|
||||
cir.br ^bb7
|
||||
^bb5(%eh_token.2 : !cir.eh_token):
|
||||
%ct2, %exn2 = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!s32i>)
|
||||
cir.end_catch %ct2 : !cir.catch_token
|
||||
cir.br ^bb7
|
||||
^bb6(%eh_token.3 : !cir.eh_token):
|
||||
%ct3, %exn3 = cir.begin_catch %eh_token.3 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
|
||||
cir.end_catch %ct3 : !cir.catch_token
|
||||
cir.br ^bb7
|
||||
^bb7:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_multiple_typed_and_catch_all()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: ^[[NORMAL]]:
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Landing pad with typed catches AND catch-all.
|
||||
// CHECK: ^[[UNWIND]]:
|
||||
// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception [@_ZTISt9exception, @_ZTIi]
|
||||
// CHECK: cir.br ^[[CMP1_BLOCK:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch: branch to comparison chain.
|
||||
// CHECK: ^[[CMP1_BLOCK]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.br ^[[CMP1:bb[0-9]+]]
|
||||
//
|
||||
// First type comparison (exception type).
|
||||
// CHECK: ^[[CMP1]](%[[C1_EXN:.*]]: !cir.ptr<!void>, %[[C1_TID:.*]]: !u32i):
|
||||
// CHECK: %[[TID1:.*]] = cir.eh.typeid @_ZTISt9exception
|
||||
// CHECK: %[[CMP1V:.*]] = cir.cmp(eq, %[[C1_TID]], %[[TID1]])
|
||||
// CHECK: cir.brcond %[[CMP1V]] ^[[CATCH_EXN:bb[0-9]+]](%[[C1_EXN]], %[[C1_TID]] : !cir.ptr<!void>, !u32i), ^[[CMP2_BLOCK:bb[0-9]+]](%[[C1_EXN]], %[[C1_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Second type comparison (int type).
|
||||
// CHECK: ^[[CMP2_BLOCK]](%[[C2_EXN:.*]]: !cir.ptr<!void>, %[[C2_TID:.*]]: !u32i):
|
||||
// CHECK: %[[TID2:.*]] = cir.eh.typeid @_ZTIi
|
||||
// CHECK: %[[CMP2:.*]] = cir.cmp(eq, %[[C2_TID]], %[[TID2]])
|
||||
// CHECK: cir.brcond %[[CMP2]] ^[[CATCH_INT:bb[0-9]+]](%[[C2_EXN]], %[[C2_TID]] : !cir.ptr<!void>, !u32i), ^[[CATCH_ALL:bb[0-9]+]](%[[C2_EXN]], %[[C2_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Exception catch handler.
|
||||
// CHECK: ^[[CATCH_EXN]](%[[CE_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[CE_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// Int catch handler.
|
||||
// CHECK: ^[[CATCH_INT]](%[[CI_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[CI_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// Catch-all handler.
|
||||
// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: Try-catch with cleanup + catch handler.
|
||||
// Two landing pads: one without cleanup (ctor), one with cleanup (doSomething).
|
||||
// cleanup ops are removed, cleanup code stays.
|
||||
cir.func @test_catch_with_cleanup() {
|
||||
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
|
||||
cir.try_call @ctor(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
^bb1:
|
||||
cir.try_call @doSomething(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
^bb2:
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.br ^bb8
|
||||
^bb3:
|
||||
%1 = cir.eh.initiate : !cir.eh_token
|
||||
cir.br ^bb6(%1 : !cir.eh_token)
|
||||
^bb4:
|
||||
%2 = cir.eh.initiate cleanup : !cir.eh_token
|
||||
cir.br ^bb5(%2 : !cir.eh_token)
|
||||
^bb5(%eh_token : !cir.eh_token):
|
||||
%3 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.end_cleanup %3 : !cir.cleanup_token
|
||||
cir.br ^bb6(%eh_token : !cir.eh_token)
|
||||
^bb6(%eh_token.1 : !cir.eh_token):
|
||||
cir.eh.dispatch %eh_token.1 : !cir.eh_token [
|
||||
catch_all : ^bb7
|
||||
]
|
||||
^bb7(%eh_token.2 : !cir.eh_token):
|
||||
%ct, %exn = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
|
||||
cir.end_catch %ct : !cir.catch_token
|
||||
cir.br ^bb8
|
||||
^bb8:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_catch_with_cleanup()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
//
|
||||
// Entry: ctor call.
|
||||
// CHECK: cir.try_call @ctor(%{{.*}}) ^[[AFTER_CTOR:bb[0-9]+]], ^[[CTOR_UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// After ctor: doSomething call.
|
||||
// CHECK: ^[[AFTER_CTOR]]:
|
||||
// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL_CLEANUP:bb[0-9]+]], ^[[DO_UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// Normal cleanup.
|
||||
// CHECK: ^[[NORMAL_CLEANUP]]:
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Ctor unwind: landing pad without cleanup.
|
||||
// CHECK: ^[[CTOR_UNWIND]]:
|
||||
// CHECK: %[[E1:.*]], %[[T1:.*]] = cir.eh.inflight_exception
|
||||
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[E1]], %[[T1]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// doSomething unwind: landing pad with cleanup.
|
||||
// CHECK: ^[[DO_UNWIND]]:
|
||||
// CHECK: %[[E2:.*]], %[[T2:.*]] = cir.eh.inflight_exception cleanup
|
||||
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[E2]], %[[T2]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// EH cleanup: begin_cleanup and end_cleanup are REMOVED, dtor call stays.
|
||||
// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
|
||||
// CHECK-NOT: cir.begin_cleanup
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK-NOT: cir.end_cleanup
|
||||
// CHECK: cir.br ^[[DISPATCH]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch: catch-all handler (direct branch).
|
||||
// CHECK: ^[[DISPATCH]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
|
||||
// CHECK: cir.br ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Catch-all handler.
|
||||
// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: Try-catch with cleanup + typed catch handler.
|
||||
// Like test_catch_with_cleanup but with a typed catch instead of catch-all.
|
||||
// Having a typed catch in the dispatch causes both eh.initiate sites to
|
||||
// include @_ZTISt9exception in their catch_type_list.
|
||||
cir.func @test_typed_catch_with_cleanup() {
|
||||
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init] {alignment = 4 : i64}
|
||||
cir.try_call @ctor(%0) ^bb1, ^bb3 : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
^bb1:
|
||||
cir.try_call @doSomething(%0) ^bb2, ^bb4 : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
^bb2:
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.br ^bb9
|
||||
^bb3:
|
||||
%1 = cir.eh.initiate : !cir.eh_token
|
||||
cir.br ^bb6(%1 : !cir.eh_token)
|
||||
^bb4:
|
||||
%2 = cir.eh.initiate cleanup : !cir.eh_token
|
||||
cir.br ^bb5(%2 : !cir.eh_token)
|
||||
^bb5(%eh_token : !cir.eh_token):
|
||||
%3 = cir.begin_cleanup %eh_token : !cir.eh_token -> !cir.cleanup_token
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.end_cleanup %3 : !cir.cleanup_token
|
||||
cir.br ^bb6(%eh_token : !cir.eh_token)
|
||||
^bb6(%eh_token.1 : !cir.eh_token):
|
||||
cir.eh.dispatch %eh_token.1 : !cir.eh_token [
|
||||
catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb7,
|
||||
unwind : ^bb8
|
||||
]
|
||||
^bb7(%eh_token.2 : !cir.eh_token):
|
||||
%ct, %exn = cir.begin_catch %eh_token.2 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
|
||||
cir.end_catch %ct : !cir.catch_token
|
||||
cir.br ^bb9
|
||||
^bb8(%eh_token.3 : !cir.eh_token):
|
||||
cir.resume %eh_token.3 : !cir.eh_token
|
||||
^bb9:
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func @test_typed_catch_with_cleanup()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
//
|
||||
// Entry: ctor call.
|
||||
// CHECK: cir.try_call @ctor(%{{.*}}) ^[[AFTER_CTOR:bb[0-9]+]], ^[[CTOR_UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// After ctor: doSomething call.
|
||||
// CHECK: ^[[AFTER_CTOR]]:
|
||||
// CHECK: cir.try_call @doSomething(%{{.*}}) ^[[NORMAL_CLEANUP:bb[0-9]+]], ^[[DO_UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// Normal cleanup.
|
||||
// CHECK: ^[[NORMAL_CLEANUP]]:
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK: cir.br ^[[RETURN:bb[0-9]+]]
|
||||
//
|
||||
// Ctor unwind: landing pad without cleanup; typed catch list from shared dispatch.
|
||||
// CHECK: ^[[CTOR_UNWIND]]:
|
||||
// CHECK: %[[E1:.*]], %[[T1:.*]] = cir.eh.inflight_exception [@_ZTISt9exception]
|
||||
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[E1]], %[[T1]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// doSomething unwind: landing pad with cleanup and same typed catch list.
|
||||
// CHECK: ^[[DO_UNWIND]]:
|
||||
// CHECK: %[[E2:.*]], %[[T2:.*]] = cir.eh.inflight_exception cleanup [@_ZTISt9exception]
|
||||
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[E2]], %[[T2]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// EH cleanup: begin_cleanup and end_cleanup are REMOVED, dtor call stays.
|
||||
// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
|
||||
// CHECK-NOT: cir.begin_cleanup
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK-NOT: cir.end_cleanup
|
||||
// CHECK: cir.br ^[[DISPATCH]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch: branch to comparison chain.
|
||||
// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
|
||||
//
|
||||
// Comparison: typeid comparison for std::exception, with unwind fallthrough.
|
||||
// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
|
||||
// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
|
||||
// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
|
||||
// CHECK: cir.brcond %[[CMP]] ^[[CATCH:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[UNWIND_HANDLER:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Typed catch handler.
|
||||
// CHECK: ^[[CATCH]](%[[C_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[C_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
// CHECK: cir.br ^[[RETURN]]
|
||||
//
|
||||
// Unwind handler: resume.flat.
|
||||
// CHECK: ^[[UNWIND_HANDLER]](%[[UW_EXN:.*]]: !cir.ptr<!void>, %[[UW_TID:.*]]: !u32i):
|
||||
// CHECK: cir.resume.flat %[[UW_EXN]], %[[UW_TID]]
|
||||
//
|
||||
// CHECK: ^[[RETURN]]:
|
||||
// CHECK: cir.return
|
||||
|
||||
// Test: try-catch with a local variable that has a non-trivial destructor.
|
||||
// The cleanup path calls ~S() then falls through to the catch-all dispatch.
|
||||
// begin_cleanup/end_cleanup are removed; typed catch + catch-all dispatch is lowered.
|
||||
cir.func no_inline dso_local @test_catch_with_cleanup_no_ctor() personality(@__gxx_personality_v0) {
|
||||
cir.br ^bb1
|
||||
^bb1: // pred: ^bb0
|
||||
%0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["s"] {alignment = 1 : i64}
|
||||
cir.br ^bb2
|
||||
^bb2: // pred: ^bb1
|
||||
cir.try_call @mayThrow() ^bb3, ^bb4 : () -> ()
|
||||
^bb3: // pred: ^bb2
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.br ^bb9
|
||||
^bb4: // pred: ^bb2
|
||||
%1 = cir.eh.initiate cleanup : !cir.eh_token
|
||||
cir.br ^bb5(%1 : !cir.eh_token)
|
||||
^bb5(%2: !cir.eh_token): // pred: ^bb4
|
||||
%3 = cir.begin_cleanup %2 : !cir.eh_token -> !cir.cleanup_token
|
||||
cir.call @dtor(%0) nothrow : (!cir.ptr<!rec_SomeClass>) -> ()
|
||||
cir.end_cleanup %3 : !cir.cleanup_token
|
||||
cir.br ^bb6(%2 : !cir.eh_token)
|
||||
^bb6(%4: !cir.eh_token): // pred: ^bb5
|
||||
cir.eh.dispatch %4 : !cir.eh_token [
|
||||
catch(#cir.global_view<@_ZTISt9exception> : !cir.ptr<!u8i>) : ^bb7,
|
||||
catch_all : ^bb8
|
||||
]
|
||||
^bb7(%6: !cir.eh_token): // pred: ^bb6
|
||||
%catch_token2, %exn_ptr2 = cir.begin_catch %6 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!cir.ptr<!rec_exception>>)
|
||||
cir.end_catch %catch_token2 : !cir.catch_token
|
||||
cir.br ^bb9
|
||||
^bb8(%5: !cir.eh_token): // pred: ^bb6
|
||||
%catch_token, %exn_ptr = cir.begin_catch %5 : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
|
||||
cir.end_catch %catch_token : !cir.catch_token
|
||||
cir.br ^bb9
|
||||
^bb9: // preds: ^bb3, ^bb7, ^bb8
|
||||
cir.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: cir.func {{.*}} @test_catch_with_cleanup_no_ctor()
|
||||
// CHECK-SAME: personality(@__gxx_personality_v0)
|
||||
// CHECK: cir.try_call @mayThrow() ^[[NORMAL:bb[0-9]+]], ^[[UNWIND:bb[0-9]+]]
|
||||
//
|
||||
// Normal path: destructor called on success.
|
||||
// CHECK: ^[[NORMAL]]:
|
||||
// CHECK: cir.call @dtor
|
||||
//
|
||||
// Unwind: initiate cleanup → inflight_exception cleanup with std::exception type.
|
||||
// CHECK: ^[[UNWIND]]:
|
||||
// CHECK: %[[EXN:.*]], %[[TID:.*]] = cir.eh.inflight_exception cleanup [@_ZTISt9exception]
|
||||
// CHECK: cir.br ^[[CLEANUP:bb[0-9]+]](%[[EXN]], %[[TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Cleanup: begin_cleanup/end_cleanup removed, destructor call stays.
|
||||
// CHECK: ^[[CLEANUP]](%[[CL_EXN:.*]]: !cir.ptr<!void>, %[[CL_TID:.*]]: !u32i):
|
||||
// CHECK-NOT: cir.begin_cleanup
|
||||
// CHECK: cir.call @dtor
|
||||
// CHECK-NOT: cir.end_cleanup
|
||||
// CHECK: cir.br ^[[DISPATCH:bb[0-9]+]](%[[CL_EXN]], %[[CL_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Dispatch: branch to comparison chain.
|
||||
// CHECK: ^[[DISPATCH]](%{{.*}}: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.br ^[[CMP_BLOCK:bb[0-9]+]]
|
||||
//
|
||||
// Comparison: typeid comparison for std::exception, with catch-all fallthrough.
|
||||
// CHECK: ^[[CMP_BLOCK]](%[[D_EXN:.*]]: !cir.ptr<!void>, %[[D_TID:.*]]: !u32i):
|
||||
// CHECK: %[[TYPE_ID:.*]] = cir.eh.typeid @_ZTISt9exception
|
||||
// CHECK: %[[CMP:.*]] = cir.cmp(eq, %[[D_TID]], %[[TYPE_ID]])
|
||||
// CHECK: cir.brcond %[[CMP]] ^[[TYPED:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i), ^[[CATCH_ALL:bb[0-9]+]](%[[D_EXN]], %[[D_TID]] : !cir.ptr<!void>, !u32i)
|
||||
//
|
||||
// Typed catch for std::exception (^bb7 in source).
|
||||
// CHECK: ^[[TYPED]](%[[T_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[T_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
//
|
||||
// Catch-all handler (^bb8 in source).
|
||||
// CHECK: ^[[CATCH_ALL]](%[[CA_EXN:.*]]: !cir.ptr<!void>, %{{.*}}: !u32i):
|
||||
// CHECK: cir.call @__cxa_begin_catch(%[[CA_EXN]])
|
||||
// CHECK: cir.call @__cxa_end_catch()
|
||||
|
||||
// Verify that runtime function declarations are added.
|
||||
// CHECK: cir.func private @__gxx_personality_v0(...)
|
||||
// CHECK: cir.func private @__cxa_begin_catch(!cir.ptr<!void>)
|
||||
// CHECK: cir.func private @__cxa_end_catch()
|
||||
|
||||
cir.func private @mayThrow()
|
||||
cir.func private @ctor(!cir.ptr<!rec_SomeClass>)
|
||||
cir.func private @dtor(!cir.ptr<!rec_SomeClass>) attributes {nothrow}
|
||||
cir.func private @doSomething(!cir.ptr<!rec_SomeClass>)
|
||||
cir.global "private" constant external @_ZTISt9exception : !cir.ptr<!u8i>
|
||||
cir.global "private" constant external @_ZTIi : !cir.ptr<!u8i>
|
||||
|
||||
} // end module
|
||||
@ -55,6 +55,10 @@ int main(int argc, char **argv) {
|
||||
return mlir::createCIRFlattenCFGPass();
|
||||
});
|
||||
|
||||
::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
return mlir::createCIREHABILoweringPass();
|
||||
});
|
||||
|
||||
::mlir::registerPass([]() -> std::unique_ptr<::mlir::Pass> {
|
||||
return mlir::createHoistAllocasPass();
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user