Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRDialect.td
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def CIR_Dialect : Dialect {
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
static llvm::StringRef getGlobalDtorsAttrName() { return "cir.global_dtors"; }
static llvm::StringRef getOperandSegmentSizesAttrName() { return "operandSegmentSizes"; }

void registerAttributes();
void registerTypes();
Expand Down
94 changes: 93 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2580,7 +2580,7 @@ def CIR_FuncOp : CIR_Op<"func", [
}

//===----------------------------------------------------------------------===//
// CallOp
// CallOp and TryCallOp
//===----------------------------------------------------------------------===//

def CIR_SideEffect : CIR_I32EnumAttr<
Expand Down Expand Up @@ -2707,6 +2707,98 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
];
}

def CIR_TryCallOp : CIR_CallOpBase<"try_call",[
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably have a verifier. The two destinations need to be valid blocks in the same region as the caller, and the landing pad destination needs to be a landing pad (though I'm not sure exactly how to verify that and it probably can't be done yet).

DeclareOpInterfaceMethods<BranchOpInterface>,
Terminator, AttrSizedOperandSegments
]> {
let summary = "try_call operation";

let description = [{
Mostly similar to cir.call but requires two destination
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description should mention that this operation is only used after the CFG flattening pass. (That's true, right?) Perhaps describe how a call in a try-block is represented in the initial CIR generated, and then describe how and why it becomes a try_call.

branches, one for handling exceptions in case its thrown and
the other one to follow on regular control-flow.

Example:

```mlir
// Direct call
%result = cir.try_call @division(%a, %b) ^continue, ^landing_pad
: (f32, f32) -> f32
```
}];

let arguments = !con((ins
Variadic<CIR_AnyType>:$contOperands,
Variadic<CIR_AnyType>:$landingPadOperands
Comment on lines +2731 to +2732
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these arguments? Are they operands that are passed to the continue and landing pad blocks or are they the continue and landing pad blocks themselves? If the latter, I don't understand why they are variadic and any type. If the former, are they even used?

), commonArgs);

let results = (outs Optional<CIR_AnyType>:$result);
let successors = (successor AnySuccessor:$cont, AnySuccessor:$landing_pad);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destinations are referred to as the normal destination and the unwind destination in the LLVM invoke instruction. I think it would be helpful to use the same terminology here.


let skipDefaultBuilders = 1;

let builders = [
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
"mlir::Block *":$cont, "mlir::Block *":$landing_pad,
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"mlir::ValueRange", "{}">:$contOperands,
CArg<"mlir::ValueRange", "{}">:$landingPadOperands,
CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
$_state.addOperands(operands);
if (callee)
$_state.addAttribute("callee", callee);
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);

$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));

// Handle branches
$_state.addOperands(contOperands);
$_state.addOperands(landingPadOperands);
// The TryCall ODS layout is: cont, landing_pad, operands.
llvm::copy(::llvm::ArrayRef<int32_t>({
static_cast<int32_t>(contOperands.size()),
static_cast<int32_t>(landingPadOperands.size()),
static_cast<int32_t>(operands.size())
}),
odsState.getOrAddProperties<Properties>().operandSegmentSizes.begin());
$_state.addSuccessors(cont);
$_state.addSuccessors(landing_pad);
}]>,
OpBuilder<(ins "mlir::Value":$ind_target,
"FuncType":$fn_type,
"mlir::Block *":$cont, "mlir::Block *":$landing_pad,
CArg<"mlir::ValueRange", "{}">:$operands,
CArg<"mlir::ValueRange", "{}">:$contOperands,
CArg<"mlir::ValueRange", "{}">:$landingPadOperands,
CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
::llvm::SmallVector<mlir::Value, 4> finalCallOperands({ind_target});
finalCallOperands.append(operands.begin(), operands.end());
$_state.addOperands(finalCallOperands);

if (!fn_type.hasVoidReturn())
$_state.addTypes(fn_type.getReturnType());

$_state.addAttribute("side_effect",
SideEffectAttr::get($_builder.getContext(), sideEffect));

// Handle branches
$_state.addOperands(contOperands);
$_state.addOperands(landingPadOperands);
// The TryCall ODS layout is: cont, landing_pad, operands.
llvm::copy(::llvm::ArrayRef<int32_t>({
static_cast<int32_t>(contOperands.size()),
static_cast<int32_t>(landingPadOperands.size()),
static_cast<int32_t>(finalCallOperands.size())
}),
odsState.getOrAddProperties<Properties>().operandSegmentSizes.begin());
$_state.addSuccessors(cont);
$_state.addSuccessors(landing_pad);
}]>
];
}

//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
Expand Down
197 changes: 191 additions & 6 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,13 +701,78 @@ unsigned cir::CallOp::getNumArgOperands() {
return this->getOperation()->getNumOperands();
}

static mlir::ParseResult
parseTryCallBranches(mlir::OpAsmParser &parser, mlir::OperationState &result,
llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand>
&continueOperands,
llvm::SmallVectorImpl<mlir::OpAsmParser::UnresolvedOperand>
&landingPadOperands,
llvm::SmallVectorImpl<mlir::Type> &continueTypes,
llvm::SmallVectorImpl<mlir::Type> &landingPadTypes,
llvm::SMLoc &continueOperandsLoc,
llvm::SMLoc &landingPadOperandsLoc) {
mlir::Block *continueSuccessor = nullptr;
mlir::Block *landingPadSuccessor = nullptr;

if (parser.parseSuccessor(continueSuccessor))
return mlir::failure();

if (mlir::succeeded(parser.parseOptionalLParen())) {
continueOperandsLoc = parser.getCurrentLocation();
if (parser.parseOperandList(continueOperands))
return mlir::failure();
if (parser.parseColon())
return mlir::failure();

if (parser.parseTypeList(continueTypes))
return mlir::failure();
if (parser.parseRParen())
return mlir::failure();
}

if (parser.parseComma())
return mlir::failure();

if (parser.parseSuccessor(landingPadSuccessor))
return mlir::failure();

if (mlir::succeeded(parser.parseOptionalLParen())) {
landingPadOperandsLoc = parser.getCurrentLocation();
if (parser.parseOperandList(landingPadOperands))
return mlir::failure();
if (parser.parseColon())
return mlir::failure();

if (parser.parseTypeList(landingPadTypes))
return mlir::failure();
if (parser.parseRParen())
return mlir::failure();
}

if (parser.parseOptionalAttrDict(result.attributes))
return mlir::failure();

result.addSuccessors(continueSuccessor);
result.addSuccessors(landingPadSuccessor);
return mlir::success();
}

static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
mlir::OperationState &result,
bool hasDestinationBlocks = false) {
llvm::SmallVector<mlir::OpAsmParser::UnresolvedOperand, 4> ops;
llvm::SMLoc opsLoc;
mlir::FlatSymbolRefAttr calleeAttr;
llvm::ArrayRef<mlir::Type> allResultTypes;

// TryCall control flow related
llvm::SmallVector<mlir::OpAsmParser::UnresolvedOperand, 4> continueOperands;
llvm::SMLoc continueOperandsLoc;
llvm::SmallVector<mlir::Type, 1> continueTypes;
llvm::SmallVector<mlir::OpAsmParser::UnresolvedOperand, 4> landingPadOperands;
llvm::SMLoc landingPadOperandsLoc;
llvm::SmallVector<mlir::Type, 1> landingPadTypes;

// If we cannot parse a string callee, it means this is an indirect call.
if (!parser
.parseOptionalAttribute(calleeAttr, CIRDialect::getCalleeAttrName(),
Expand All @@ -729,6 +794,14 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.parseRParen())
return mlir::failure();

if (hasDestinationBlocks &&
parseTryCallBranches(parser, result, continueOperands, landingPadOperands,
continueTypes, landingPadTypes, continueOperandsLoc,
landingPadOperandsLoc)
.failed()) {
return ::mlir::failure();
}

if (parser.parseOptionalKeyword("nothrow").succeeded())
result.addAttribute(CIRDialect::getNoThrowAttrName(),
mlir::UnitAttr::get(parser.getContext()));
Expand Down Expand Up @@ -761,14 +834,34 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.resolveOperands(ops, opsFnTy.getInputs(), opsLoc, result.operands))
return mlir::failure();

if (hasDestinationBlocks) {
// The TryCall ODS layout is: cont, landing_pad, operands.
llvm::copy(::llvm::ArrayRef<int32_t>(
{static_cast<int32_t>(continueOperands.size()),
static_cast<int32_t>(landingPadOperands.size()),
static_cast<int32_t>(ops.size())}),
result.getOrAddProperties<cir::TryCallOp::Properties>()
.operandSegmentSizes.begin());

if (parser.resolveOperands(continueOperands, continueTypes,
continueOperandsLoc, result.operands))
return ::mlir::failure();

if (parser.resolveOperands(landingPadOperands, landingPadTypes,
landingPadOperandsLoc, result.operands))
return ::mlir::failure();
}

return mlir::success();
}

static void printCallCommon(mlir::Operation *op,
mlir::FlatSymbolRefAttr calleeSym,
mlir::Value indirectCallee,
mlir::OpAsmPrinter &printer, bool isNothrow,
cir::SideEffect sideEffect) {
cir::SideEffect sideEffect,
mlir::Block *cont = nullptr,
mlir::Block *landingPad = nullptr) {
printer << ' ';

auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
Expand All @@ -782,8 +875,35 @@ static void printCallCommon(mlir::Operation *op,
assert(indirectCallee);
printer << indirectCallee;
}

printer << "(" << ops << ")";

if (cont) {
assert(landingPad && "expected two successors");
auto tryCall = dyn_cast<cir::TryCallOp>(op);
assert(tryCall && "regular calls do not branch");
printer << ' ' << tryCall.getCont();
if (!tryCall.getContOperands().empty()) {
printer << "(";
printer << tryCall.getContOperands();
printer << ' ' << ":";
printer << ' ';
printer << tryCall.getContOperands().getTypes();
printer << ")";
}
printer << ",";
printer << ' ';
printer << tryCall.getLandingPad();
if (!tryCall.getLandingPadOperands().empty()) {
printer << "(";
printer << tryCall.getLandingPadOperands();
printer << ' ' << ":";
printer << ' ';
printer << tryCall.getLandingPadOperands().getTypes();
printer << ")";
}
}

if (isNothrow)
printer << " nothrow";

Expand All @@ -793,10 +913,11 @@ static void printCallCommon(mlir::Operation *op,
printer << ")";
}

printer.printOptionalAttrDict(op->getAttrs(),
{CIRDialect::getCalleeAttrName(),
CIRDialect::getNoThrowAttrName(),
CIRDialect::getSideEffectAttrName()});
llvm::SmallVector<::llvm::StringRef, 4> elidedAttrs = {
CIRDialect::getCalleeAttrName(), CIRDialect::getNoThrowAttrName(),
CIRDialect::getSideEffectAttrName(),
CIRDialect::getOperandSegmentSizesAttrName()};
printer.printOptionalAttrDict(op->getAttrs(), elidedAttrs);

printer << " : ";
printer.printFunctionalType(op->getOperands().getTypes(),
Expand Down Expand Up @@ -878,6 +999,70 @@ cir::CallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return verifyCallCommInSymbolUses(*this, symbolTable);
}

//===----------------------------------------------------------------------===//
// TryCallOp
//===----------------------------------------------------------------------===//

mlir::OperandRange cir::TryCallOp::getArgOperands() {
if (isIndirect())
return getArgs().drop_front(1);
return getArgs();
}

mlir::MutableOperandRange cir::TryCallOp::getArgOperandsMutable() {
mlir::MutableOperandRange args = getArgsMutable();
if (isIndirect())
return args.slice(1, args.size() - 1);
return args;
}

mlir::Value cir::TryCallOp::getIndirectCall() {
assert(isIndirect());
return getOperand(0);
}

/// Return the operand at index 'i'.
Value cir::TryCallOp::getArgOperand(unsigned i) {
if (isIndirect())
++i;
return getOperand(i);
}

/// Return the number of operands.
unsigned cir::TryCallOp::getNumArgOperands() {
if (isIndirect())
return this->getOperation()->getNumOperands() - 1;
return this->getOperation()->getNumOperands();
}

LogicalResult
cir::TryCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return verifyCallCommInSymbolUses(*this, symbolTable);
}

mlir::ParseResult cir::TryCallOp::parse(mlir::OpAsmParser &parser,
mlir::OperationState &result) {
return parseCallCommon(parser, result, /*hasDestinationBlocks=*/true);
}

void cir::TryCallOp::print(::mlir::OpAsmPrinter &p) {
mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
cir::SideEffect sideEffect = getSideEffect();
printCallCommon(*this, getCalleeAttr(), indirectCallee, p, getNothrow(),
sideEffect, getCont(), getLandingPad());
}

mlir::SuccessorOperands cir::TryCallOp::getSuccessorOperands(unsigned index) {
assert(index < getNumSuccessors() && "invalid successor index");
if (index == 0)
return SuccessorOperands(getContOperandsMutable());
if (index == 1)
return SuccessorOperands(getLandingPadOperandsMutable());

// index == 2
return SuccessorOperands(getArgOperandsMutable());
}

//===----------------------------------------------------------------------===//
// ReturnOp
//===----------------------------------------------------------------------===//
Expand Down
Loading
Loading