forked from OSchip/llvm-project
[MLIR] Added llvm.invoke and llvm.landingpad
Summary:
I have tried to implement `llvm.invoke` and `llvm.landingpad`.
# `llvm.invoke` is similar to `llvm.call` with two successors added, the first one is the normal label and the second one is unwind label.
# `llvm.launchpad` takes a variable number of args with either `catch` or `filter` associated with them. Catch clauses are not array types and filter clauses are array types. This is same as the criteria used by LLVM (4f82af81a0/llvm/include/llvm/IR/Instructions.h (L2866)
)
Examples:
LLVM IR
```
define i32 @caller(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
invoke i32 @foo(i32 2) to label %success unwind label %fail
success:
ret i32 2
fail:
landingpad {i8*, i32} catch i8** @_ZTIi catch i8** null catch i8* bitcast (i8** @_ZTIi to i8*) filter [1 x i8] [ i8 1 ]
ret i32 3
}
```
MLIR LLVM Dialect
```
llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 {
%0 = llvm.mlir.constant(3 : i32) : !llvm.i32
%1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]">
%2 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
%3 = llvm.bitcast %2 : !llvm<"i8**"> to !llvm<"i8*">
%4 = llvm.mlir.null : !llvm<"i8**">
%5 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
%6 = llvm.mlir.constant(2 : i32) : !llvm.i32
%7 = llvm.invoke @foo(%6) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32
^bb1: // pred: ^bb0
llvm.return %6 : !llvm.i32
^bb2: // pred: ^bb0
%8 = llvm.landingpad (catch %5 : !llvm<"i8**">) (catch %4 : !llvm<"i8**">) (catch %3 : !llvm<"i8*">) (filter %1 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
llvm.return %0 : !llvm.i32
}
```
Signed-off-by: Shraiysh Vaishay <cs17btech11050@iith.ac.in>
Differential Revision: https://reviews.llvm.org/D72006
This commit is contained in:
parent
06e12893ff
commit
d242aa245c
|
@ -315,6 +315,41 @@ def LLVM_FPExtOp : LLVM_CastOp<"fpext", "CreateFPExt">;
|
|||
def LLVM_FPTruncOp : LLVM_CastOp<"fptrunc", "CreateFPTrunc">;
|
||||
|
||||
// Call-related operations.
|
||||
def LLVM_InvokeOp : LLVM_Op<"invoke", [Terminator]>,
|
||||
Arguments<(ins OptionalAttr<FlatSymbolRefAttr>:$callee,
|
||||
Variadic<LLVM_Type>)>,
|
||||
Results<(outs Variadic<LLVM_Type>)> {
|
||||
let builders = [OpBuilder<
|
||||
"Builder *b, OperationState &result, ArrayRef<Type> tys, "
|
||||
"FlatSymbolRefAttr callee, ValueRange ops, Block* normal, "
|
||||
"ValueRange normalOps, Block* unwind, ValueRange unwindOps",
|
||||
[{
|
||||
result.addAttribute("callee", callee);
|
||||
build(b, result, tys, ops, normal, normalOps, unwind, unwindOps);
|
||||
}]>,
|
||||
OpBuilder<
|
||||
"Builder *b, OperationState &result, ArrayRef<Type> tys, "
|
||||
"ValueRange ops, Block* normal, "
|
||||
"ValueRange normalOps, Block* unwind, ValueRange unwindOps",
|
||||
[{
|
||||
result.addTypes(tys);
|
||||
result.addOperands(ops);
|
||||
result.addSuccessor(normal, normalOps);
|
||||
result.addSuccessor(unwind, unwindOps);
|
||||
}]>];
|
||||
let verifier = [{ return ::verify(*this); }];
|
||||
let parser = [{ return parseInvokeOp(parser, result); }];
|
||||
let printer = [{ printInvokeOp(p, *this); }];
|
||||
}
|
||||
|
||||
def LLVM_LandingpadOp : LLVM_OneResultOp<"landingpad">,
|
||||
Arguments<(ins UnitAttr:$cleanup,
|
||||
Variadic<LLVM_Type>)> {
|
||||
let verifier = [{ return ::verify(*this); }];
|
||||
let parser = [{ return parseLandingpadOp(parser, result); }];
|
||||
let printer = [{ printLandingpadOp(p, *this); }];
|
||||
}
|
||||
|
||||
def LLVM_CallOp : LLVM_Op<"call">,
|
||||
Arguments<(ins OptionalAttr<FlatSymbolRefAttr>:$callee,
|
||||
Variadic<LLVM_Type>)>,
|
||||
|
|
|
@ -272,6 +272,231 @@ static ParseResult parseStoreOp(OpAsmParser &parser, OperationState &result) {
|
|||
return success();
|
||||
}
|
||||
|
||||
///===----------------------------------------------------------------------===//
|
||||
/// Verifying/Printing/Parsing for LLVM::InvokeOp.
|
||||
///===----------------------------------------------------------------------===//
|
||||
|
||||
static LogicalResult verify(InvokeOp op) {
|
||||
if (op.getNumResults() > 1)
|
||||
return op.emitOpError("must have 0 or 1 result");
|
||||
if (op.getNumSuccessors() != 2)
|
||||
return op.emitOpError("must have normal and unwind destinations");
|
||||
|
||||
if (op.getSuccessor(1)->empty())
|
||||
return op.emitError(
|
||||
"must have at least one operation in unwind destination");
|
||||
|
||||
// In unwind destination, first operation must be LandingpadOp
|
||||
if (!isa<LandingpadOp>(op.getSuccessor(1)->front()))
|
||||
return op.emitError("first operation in unwind destination should be a "
|
||||
"llvm.landingpad operation");
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
static void printInvokeOp(OpAsmPrinter &p, InvokeOp &op) {
|
||||
auto callee = op.callee();
|
||||
bool isDirect = callee.hasValue();
|
||||
|
||||
p << op.getOperationName() << ' ';
|
||||
|
||||
// Either function name or pointer
|
||||
if (isDirect)
|
||||
p.printSymbolName(callee.getValue());
|
||||
else
|
||||
p << op.getOperand(0);
|
||||
|
||||
p << '(' << op.getOperands().drop_front(isDirect ? 0 : 1) << ')';
|
||||
p << " to ";
|
||||
p.printSuccessorAndUseList(op.getOperation(), 0);
|
||||
p << " unwind ";
|
||||
p.printSuccessorAndUseList(op.getOperation(), 1);
|
||||
|
||||
p.printOptionalAttrDict(op.getAttrs(), {"callee"});
|
||||
|
||||
SmallVector<Type, 8> argTypes(
|
||||
llvm::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1));
|
||||
|
||||
p << " : "
|
||||
<< FunctionType::get(argTypes, op.getResultTypes(), op.getContext());
|
||||
}
|
||||
|
||||
/// <operation> ::= `llvm.invoke` (function-id | ssa-use) `(` ssa-use-list `)`
|
||||
/// `to` bb-id (`[` ssa-use-and-type-list `]`)?
|
||||
/// `unwind` bb-id (`[` ssa-use-and-type-list `]`)?
|
||||
/// attribute-dict? `:` function-type
|
||||
static ParseResult parseInvokeOp(OpAsmParser &parser, OperationState &result) {
|
||||
SmallVector<OpAsmParser::OperandType, 8> operands;
|
||||
FunctionType funcType;
|
||||
SymbolRefAttr funcAttr;
|
||||
llvm::SMLoc trailingTypeLoc;
|
||||
Block *normalDest, *unwindDest;
|
||||
SmallVector<Value, 4> normalOperands, unwindOperands;
|
||||
|
||||
// Parse an operand list that will, in practice, contain 0 or 1 operand. In
|
||||
// case of an indirect call, there will be 1 operand before `(`. In case of a
|
||||
// direct call, there will be no operands and the parser will stop at the
|
||||
// function identifier without complaining.
|
||||
if (parser.parseOperandList(operands))
|
||||
return failure();
|
||||
bool isDirect = operands.empty();
|
||||
|
||||
// Optionally parse a function identifier.
|
||||
if (isDirect && parser.parseAttribute(funcAttr, "callee", result.attributes))
|
||||
return failure();
|
||||
|
||||
if (parser.parseOperandList(operands, OpAsmParser::Delimiter::Paren) ||
|
||||
parser.parseKeyword("to") ||
|
||||
parser.parseSuccessorAndUseList(normalDest, normalOperands) ||
|
||||
parser.parseKeyword("unwind") ||
|
||||
parser.parseSuccessorAndUseList(unwindDest, unwindOperands) ||
|
||||
parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
|
||||
parser.getCurrentLocation(&trailingTypeLoc) || parser.parseType(funcType))
|
||||
return failure();
|
||||
|
||||
if (isDirect) {
|
||||
// Make sure types match.
|
||||
if (parser.resolveOperands(operands, funcType.getInputs(),
|
||||
parser.getNameLoc(), result.operands))
|
||||
return failure();
|
||||
result.addTypes(funcType.getResults());
|
||||
} else {
|
||||
// Construct the LLVM IR Dialect function type that the first operand
|
||||
// should match.
|
||||
if (funcType.getNumResults() > 1)
|
||||
return parser.emitError(trailingTypeLoc,
|
||||
"expected function with 0 or 1 result");
|
||||
|
||||
Builder &builder = parser.getBuilder();
|
||||
auto *llvmDialect =
|
||||
builder.getContext()->getRegisteredDialect<LLVM::LLVMDialect>();
|
||||
LLVM::LLVMType llvmResultType;
|
||||
if (funcType.getNumResults() == 0) {
|
||||
llvmResultType = LLVM::LLVMType::getVoidTy(llvmDialect);
|
||||
} else {
|
||||
llvmResultType = funcType.getResult(0).dyn_cast<LLVM::LLVMType>();
|
||||
if (!llvmResultType)
|
||||
return parser.emitError(trailingTypeLoc,
|
||||
"expected result to have LLVM type");
|
||||
}
|
||||
|
||||
SmallVector<LLVM::LLVMType, 8> argTypes;
|
||||
argTypes.reserve(funcType.getNumInputs());
|
||||
for (Type ty : funcType.getInputs()) {
|
||||
if (auto argType = ty.dyn_cast<LLVM::LLVMType>())
|
||||
argTypes.push_back(argType);
|
||||
else
|
||||
return parser.emitError(trailingTypeLoc,
|
||||
"expected LLVM types as inputs");
|
||||
}
|
||||
|
||||
auto llvmFuncType = LLVM::LLVMType::getFunctionTy(llvmResultType, argTypes,
|
||||
/*isVarArg=*/false);
|
||||
auto wrappedFuncType = llvmFuncType.getPointerTo();
|
||||
|
||||
auto funcArguments = llvm::makeArrayRef(operands).drop_front();
|
||||
|
||||
// Make sure that the first operand (indirect callee) matches the wrapped
|
||||
// LLVM IR function type, and that the types of the other call operands
|
||||
// match the types of the function arguments.
|
||||
if (parser.resolveOperand(operands[0], wrappedFuncType, result.operands) ||
|
||||
parser.resolveOperands(funcArguments, funcType.getInputs(),
|
||||
parser.getNameLoc(), result.operands))
|
||||
return failure();
|
||||
|
||||
result.addTypes(llvmResultType);
|
||||
}
|
||||
result.addSuccessor(normalDest, normalOperands);
|
||||
result.addSuccessor(unwindDest, unwindOperands);
|
||||
return success();
|
||||
}
|
||||
|
||||
///===----------------------------------------------------------------------===//
|
||||
/// Verifying/Printing/Parsing for LLVM::LandingpadOp.
|
||||
///===----------------------------------------------------------------------===//
|
||||
|
||||
static LogicalResult verify(LandingpadOp op) {
|
||||
Value value;
|
||||
|
||||
if (!op.cleanup() && op.getOperands().empty())
|
||||
return op.emitError("landingpad instruction expects at least one clause or "
|
||||
"cleanup attribute");
|
||||
|
||||
for (unsigned idx = 0, ie = op.getNumOperands(); idx < ie; idx++) {
|
||||
value = op.getOperand(idx);
|
||||
bool isFilter = value.getType().cast<LLVMType>().isArrayTy();
|
||||
if (isFilter) {
|
||||
// FIXME: Verify filter clauses when arrays are appropriately handled
|
||||
} else {
|
||||
// catch - global addresses only.
|
||||
// Bitcast ops should have global addresses as their args.
|
||||
if (auto bcOp = dyn_cast_or_null<BitcastOp>(value.getDefiningOp())) {
|
||||
if (auto addrOp =
|
||||
dyn_cast_or_null<AddressOfOp>(bcOp.arg().getDefiningOp()))
|
||||
continue;
|
||||
return op.emitError("constant clauses expected")
|
||||
.attachNote(bcOp.getLoc())
|
||||
<< "global addresses expected as operand to "
|
||||
"bitcast used in clauses for landingpad";
|
||||
}
|
||||
// NullOp and AddressOfOp allowed
|
||||
if (dyn_cast_or_null<NullOp>(value.getDefiningOp()))
|
||||
continue;
|
||||
if (dyn_cast_or_null<AddressOfOp>(value.getDefiningOp()))
|
||||
continue;
|
||||
return op.emitError("clause #")
|
||||
<< idx << " is not a known constant - null, addressof, bitcast";
|
||||
}
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
static void printLandingpadOp(OpAsmPrinter &p, LandingpadOp &op) {
|
||||
p << op.getOperationName() << (op.cleanup() ? " cleanup " : " ");
|
||||
|
||||
// Clauses
|
||||
for (auto value : op.getOperands()) {
|
||||
// Similar to llvm - if clause is an array type then it is filter
|
||||
// clause else catch clause
|
||||
bool isArrayTy = value.getType().cast<LLVMType>().isArrayTy();
|
||||
p << '(' << (isArrayTy ? "filter " : "catch ") << value << " : "
|
||||
<< value.getType() << ") ";
|
||||
}
|
||||
|
||||
p.printOptionalAttrDict(op.getAttrs(), {"cleanup"});
|
||||
|
||||
p << ": " << op.getType();
|
||||
}
|
||||
|
||||
/// <operation> ::= `llvm.landingpad` `cleanup`?
|
||||
/// ((`catch` | `filter`) operand-type ssa-use)* attribute-dict?
|
||||
static ParseResult parseLandingpadOp(OpAsmParser &parser,
|
||||
OperationState &result) {
|
||||
// Check for cleanup
|
||||
if (succeeded(parser.parseOptionalKeyword("cleanup")))
|
||||
result.addAttribute("cleanup", parser.getBuilder().getUnitAttr());
|
||||
|
||||
// Parse clauses with types
|
||||
while (succeeded(parser.parseOptionalLParen()) &&
|
||||
(succeeded(parser.parseOptionalKeyword("filter")) ||
|
||||
succeeded(parser.parseOptionalKeyword("catch")))) {
|
||||
OpAsmParser::OperandType operand;
|
||||
Type ty;
|
||||
if (parser.parseOperand(operand) || parser.parseColon() ||
|
||||
parser.parseType(ty) ||
|
||||
parser.resolveOperand(operand, ty, result.operands) ||
|
||||
parser.parseRParen())
|
||||
return failure();
|
||||
}
|
||||
|
||||
Type type;
|
||||
if (parser.parseColon() || parser.parseType(type))
|
||||
return failure();
|
||||
|
||||
result.addTypes(type);
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Printing/parsing for LLVM::CallOp.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -76,7 +76,7 @@ private:
|
|||
/// `br` branches to `target`. Append the block arguments to attach to the
|
||||
/// generated branch op to `blockArguments`. These should be in the same order
|
||||
/// as the PHIs in `target`.
|
||||
LogicalResult processBranchArgs(llvm::BranchInst *br,
|
||||
LogicalResult processBranchArgs(llvm::Instruction *br,
|
||||
llvm::BasicBlock *target,
|
||||
SmallVectorImpl<Value> &blockArguments);
|
||||
/// Returns the standard type equivalent to be used in attributes for the
|
||||
|
@ -422,21 +422,26 @@ GlobalOp Importer::processGlobal(llvm::GlobalVariable *GV) {
|
|||
}
|
||||
|
||||
Value Importer::processConstant(llvm::Constant *c) {
|
||||
OpBuilder bEntry(currentEntryBlock, currentEntryBlock->begin());
|
||||
if (Attribute attr = getConstantAsAttr(c)) {
|
||||
// These constants can be represented as attributes.
|
||||
OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
|
||||
LLVMType type = processType(c->getType());
|
||||
if (!type)
|
||||
return nullptr;
|
||||
return instMap[c] = b.create<ConstantOp>(unknownLoc, type, attr);
|
||||
return instMap[c] = bEntry.create<ConstantOp>(unknownLoc, type, attr);
|
||||
}
|
||||
if (auto *cn = dyn_cast<llvm::ConstantPointerNull>(c)) {
|
||||
OpBuilder b(currentEntryBlock, currentEntryBlock->begin());
|
||||
LLVMType type = processType(cn->getType());
|
||||
if (!type)
|
||||
return nullptr;
|
||||
return instMap[c] = b.create<NullOp>(unknownLoc, type);
|
||||
return instMap[c] = bEntry.create<NullOp>(unknownLoc, type);
|
||||
}
|
||||
if (auto *GV = dyn_cast<llvm::GlobalVariable>(c))
|
||||
return bEntry.create<AddressOfOp>(UnknownLoc::get(context),
|
||||
processGlobal(GV),
|
||||
ArrayRef<NamedAttribute>());
|
||||
|
||||
if (auto *ce = dyn_cast<llvm::ConstantExpr>(c)) {
|
||||
llvm::Instruction *i = ce->getAsInstruction();
|
||||
OpBuilder::InsertionGuard guard(b);
|
||||
|
@ -471,16 +476,6 @@ Value Importer::processValue(llvm::Value *value) {
|
|||
return unknownInstMap[value]->getResult(0);
|
||||
}
|
||||
|
||||
if (auto *GV = dyn_cast<llvm::GlobalVariable>(value)) {
|
||||
auto global = processGlobal(GV);
|
||||
if (!global)
|
||||
return nullptr;
|
||||
return b.create<AddressOfOp>(UnknownLoc::get(context), global,
|
||||
ArrayRef<NamedAttribute>());
|
||||
}
|
||||
|
||||
// Note, constant global variables are both GlobalVariables and Constants,
|
||||
// so we handle GlobalVariables first above.
|
||||
if (auto *c = dyn_cast<llvm::Constant>(value))
|
||||
return processConstant(c);
|
||||
|
||||
|
@ -570,7 +565,7 @@ static ICmpPredicate getICmpPredicate(llvm::CmpInst::Predicate p) {
|
|||
// `br` branches to `target`. Return the branch arguments to `br`, in the
|
||||
// same order of the PHIs in `target`.
|
||||
LogicalResult
|
||||
Importer::processBranchArgs(llvm::BranchInst *br, llvm::BasicBlock *target,
|
||||
Importer::processBranchArgs(llvm::Instruction *br, llvm::BasicBlock *target,
|
||||
SmallVectorImpl<Value> &blockArguments) {
|
||||
for (auto inst = target->begin(); isa<llvm::PHINode>(inst); ++inst) {
|
||||
auto *PN = cast<llvm::PHINode>(&*inst);
|
||||
|
@ -719,6 +714,49 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) {
|
|||
v = op->getResult(0);
|
||||
return success();
|
||||
}
|
||||
case llvm::Instruction::LandingPad: {
|
||||
llvm::LandingPadInst *lpi = cast<llvm::LandingPadInst>(inst);
|
||||
SmallVector<Value, 4> ops;
|
||||
|
||||
for (unsigned i = 0, ie = lpi->getNumClauses(); i < ie; i++)
|
||||
ops.push_back(processConstant(lpi->getClause(i)));
|
||||
|
||||
b.create<LandingpadOp>(loc, processType(lpi->getType()), lpi->isCleanup(),
|
||||
ops);
|
||||
return success();
|
||||
}
|
||||
case llvm::Instruction::Invoke: {
|
||||
llvm::InvokeInst *ii = cast<llvm::InvokeInst>(inst);
|
||||
|
||||
SmallVector<Type, 2> tys;
|
||||
if (!ii->getType()->isVoidTy())
|
||||
tys.push_back(processType(inst->getType()));
|
||||
|
||||
SmallVector<Value, 4> ops;
|
||||
ops.reserve(inst->getNumOperands() + 1);
|
||||
for (auto &op : ii->arg_operands())
|
||||
ops.push_back(processValue(op.get()));
|
||||
|
||||
SmallVector<Value, 4> normalArgs, unwindArgs;
|
||||
processBranchArgs(ii, ii->getNormalDest(), normalArgs);
|
||||
processBranchArgs(ii, ii->getUnwindDest(), unwindArgs);
|
||||
|
||||
Operation *op;
|
||||
if (llvm::Function *callee = ii->getCalledFunction()) {
|
||||
op = b.create<InvokeOp>(loc, tys, b.getSymbolRefAttr(callee->getName()),
|
||||
ops, blocks[ii->getNormalDest()], normalArgs,
|
||||
blocks[ii->getUnwindDest()], unwindArgs);
|
||||
} else {
|
||||
ops.insert(ops.begin(), processValue(ii->getCalledValue()));
|
||||
op = b.create<InvokeOp>(loc, tys, ops, blocks[ii->getNormalDest()],
|
||||
normalArgs, blocks[ii->getUnwindDest()],
|
||||
unwindArgs);
|
||||
}
|
||||
|
||||
if (!ii->getType()->isVoidTy())
|
||||
v = op->getResult(0);
|
||||
return success();
|
||||
}
|
||||
case llvm::Instruction::GetElementPtr: {
|
||||
// FIXME: Support inbounds GEPs.
|
||||
llvm::GetElementPtrInst *gep = cast<llvm::GetElementPtrInst>(inst);
|
||||
|
|
|
@ -307,6 +307,34 @@ LogicalResult ModuleTranslation::convertOperation(Operation &opInst,
|
|||
return success(result->getType()->isVoidTy());
|
||||
}
|
||||
|
||||
if (auto invOp = dyn_cast<LLVM::InvokeOp>(opInst)) {
|
||||
auto operands = lookupValues(opInst.getOperands());
|
||||
ArrayRef<llvm::Value *> operandsRef(operands);
|
||||
if (auto attr = opInst.getAttrOfType<FlatSymbolRefAttr>("callee"))
|
||||
builder.CreateInvoke(functionMapping.lookup(attr.getValue()),
|
||||
blockMapping[invOp.getSuccessor(0)],
|
||||
blockMapping[invOp.getSuccessor(1)], operandsRef);
|
||||
else
|
||||
builder.CreateInvoke(
|
||||
operandsRef.front(), blockMapping[invOp.getSuccessor(0)],
|
||||
blockMapping[invOp.getSuccessor(1)], operandsRef.drop_front());
|
||||
return success();
|
||||
}
|
||||
|
||||
if (auto lpOp = dyn_cast<LLVM::LandingpadOp>(opInst)) {
|
||||
llvm::Type *ty = lpOp.getType().dyn_cast<LLVMType>().getUnderlyingType();
|
||||
llvm::LandingPadInst *lpi =
|
||||
builder.CreateLandingPad(ty, lpOp.getNumOperands());
|
||||
|
||||
// Add clauses
|
||||
for (auto operand : lookupValues(lpOp.getOperands())) {
|
||||
// All operands should be constant - checked by verifier
|
||||
if (auto constOperand = dyn_cast<llvm::Constant>(operand))
|
||||
lpi->addClause(constOperand);
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
// Emit branches. We need to look up the remapped blocks and ignore the block
|
||||
// arguments that were transformed into PHI nodes.
|
||||
if (auto brOp = dyn_cast<LLVM::BrOp>(opInst)) {
|
||||
|
|
|
@ -509,3 +509,55 @@ func @cmpxchg_failure_acq_rel(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) {
|
|||
%0 = llvm.cmpxchg %i32_ptr, %i32, %i32 acq_rel acq_rel : !llvm.i32
|
||||
llvm.return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.func @foo(!llvm.i32) -> !llvm.i32
|
||||
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
|
||||
|
||||
llvm.func @bad_landingpad(%arg0: !llvm<"i8**">) {
|
||||
%0 = llvm.mlir.constant(3 : i32) : !llvm.i32
|
||||
%1 = llvm.mlir.constant(2 : i32) : !llvm.i32
|
||||
%2 = llvm.invoke @foo(%1) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32
|
||||
^bb1: // pred: ^bb0
|
||||
llvm.return %1 : !llvm.i32
|
||||
^bb2: // pred: ^bb0
|
||||
// expected-error@+1 {{clause #0 is not a known constant - null, addressof, bitcast}}
|
||||
%3 = llvm.landingpad cleanup (catch %1 : !llvm.i32) (catch %arg0 : !llvm<"i8**">) : !llvm<"{ i8*, i32 }">
|
||||
llvm.return %0 : !llvm.i32
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.func @foo(!llvm.i32) -> !llvm.i32
|
||||
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
|
||||
|
||||
llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 {
|
||||
%0 = llvm.mlir.constant(1 : i32) : !llvm.i32
|
||||
%1 = llvm.alloca %0 x !llvm<"i8*"> : (!llvm.i32) -> !llvm<"i8**">
|
||||
// expected-note@+1 {{global addresses expected as operand to bitcast used in clauses for landingpad}}
|
||||
%2 = llvm.bitcast %1 : !llvm<"i8**"> to !llvm<"i8*">
|
||||
%3 = llvm.invoke @foo(%0) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32
|
||||
^bb1: // pred: ^bb0
|
||||
llvm.return %0 : !llvm.i32
|
||||
^bb2: // pred: ^bb0
|
||||
// expected-error@+1 {{constant clauses expected}}
|
||||
%5 = llvm.landingpad (catch %2 : !llvm<"i8*">) : !llvm<"{ i8*, i32 }">
|
||||
llvm.return %0 : !llvm.i32
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
llvm.func @foo(!llvm.i32) -> !llvm.i32
|
||||
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
|
||||
|
||||
llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 {
|
||||
%0 = llvm.mlir.constant(1 : i32) : !llvm.i32
|
||||
%1 = llvm.invoke @foo(%0) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32
|
||||
^bb1: // pred: ^bb0
|
||||
llvm.return %0 : !llvm.i32
|
||||
^bb2: // pred: ^bb0
|
||||
// expected-error@+1 {{landingpad instruction expects at least one clause or cleanup attribute}}
|
||||
%2 = llvm.landingpad : !llvm<"{ i8*, i32 }">
|
||||
llvm.return %0 : !llvm.i32
|
||||
}
|
||||
|
|
|
@ -232,3 +232,53 @@ func @cmpxchg(%ptr : !llvm<"float*">, %cmp : !llvm.float, %new : !llvm.float) {
|
|||
%0 = llvm.cmpxchg %ptr, %cmp, %new acq_rel monotonic : !llvm.float
|
||||
llvm.return
|
||||
}
|
||||
|
||||
llvm.mlir.global external constant @_ZTIi() : !llvm<"i8*">
|
||||
llvm.func @bar(!llvm<"i8*">, !llvm<"i8*">, !llvm<"i8*">)
|
||||
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
|
||||
|
||||
// CHECK-LABEL: @invokeLandingpad
|
||||
llvm.func @invokeLandingpad() -> !llvm.i32 {
|
||||
// CHECK-NEXT: %[[a0:[0-9]+]] = llvm.mlir.constant(0 : i32) : !llvm.i32
|
||||
// CHECK-NEXT: %{{[0-9]+}} = llvm.mlir.constant(3 : i32) : !llvm.i32
|
||||
// CHECK-NEXT: %[[a2:[0-9]+]] = llvm.mlir.constant("\01") : !llvm<"[1 x i8]">
|
||||
// CHECK-NEXT: %[[a3:[0-9]+]] = llvm.mlir.null : !llvm<"i8**">
|
||||
// CHECK-NEXT: %[[a4:[0-9]+]] = llvm.mlir.null : !llvm<"i8*">
|
||||
// CHECK-NEXT: %[[a5:[0-9]+]] = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
|
||||
// CHECK-NEXT: %[[a6:[0-9]+]] = llvm.bitcast %[[a5]] : !llvm<"i8**"> to !llvm<"i8*">
|
||||
// CHECK-NEXT: %[[a7:[0-9]+]] = llvm.mlir.constant(1 : i32) : !llvm.i32
|
||||
// CHECK-NEXT: %[[a8:[0-9]+]] = llvm.alloca %[[a7]] x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*">
|
||||
// CHECK-NEXT: %{{[0-9]+}} = llvm.invoke @foo(%[[a7]]) to ^bb2 unwind ^bb1 : (!llvm.i32) -> !llvm<"{ i32, double, i32 }">
|
||||
%0 = llvm.mlir.constant(0 : i32) : !llvm.i32
|
||||
%1 = llvm.mlir.constant(3 : i32) : !llvm.i32
|
||||
%2 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]">
|
||||
%3 = llvm.mlir.null : !llvm<"i8**">
|
||||
%4 = llvm.mlir.null : !llvm<"i8*">
|
||||
%5 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
|
||||
%6 = llvm.bitcast %5 : !llvm<"i8**"> to !llvm<"i8*">
|
||||
%7 = llvm.mlir.constant(1 : i32) : !llvm.i32
|
||||
%8 = llvm.alloca %7 x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*">
|
||||
%9 = llvm.invoke @foo(%7) to ^bb2 unwind ^bb1 : (!llvm.i32) -> !llvm<"{ i32, double, i32 }">
|
||||
|
||||
// CHECK-NEXT: ^bb1:
|
||||
// CHECK-NEXT: %{{[0-9]+}} = llvm.landingpad cleanup (catch %[[a3]] : !llvm<"i8**">) (catch %[[a6]] : !llvm<"i8*">) (filter %[[a2]] : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
|
||||
// CHECK-NEXT: llvm.br ^bb3
|
||||
^bb1:
|
||||
%10 = llvm.landingpad cleanup (catch %3 : !llvm<"i8**">) (catch %6 : !llvm<"i8*">) (filter %2 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
|
||||
llvm.br ^bb3
|
||||
|
||||
// CHECK-NEXT: ^bb2:
|
||||
// CHECK-NEXT: llvm.return %[[a7]] : !llvm.i32
|
||||
^bb2:
|
||||
llvm.return %7 : !llvm.i32
|
||||
|
||||
// CHECK-NEXT: ^bb3:
|
||||
// CHECK-NEXT: llvm.invoke @bar(%[[a8]], %[[a6]], %[[a4]]) to ^bb2 unwind ^bb1 : (!llvm<"i8*">, !llvm<"i8*">, !llvm<"i8*">) -> ()
|
||||
^bb3:
|
||||
llvm.invoke @bar(%8, %6, %4) to ^bb2 unwind ^bb1 : (!llvm<"i8*">, !llvm<"i8*">, !llvm<"i8*">) -> ()
|
||||
|
||||
// CHECK-NEXT: ^bb4:
|
||||
// CHECK-NEXT: llvm.return %[[a0]] : !llvm.i32
|
||||
^bb4:
|
||||
llvm.return %0 : !llvm.i32
|
||||
}
|
||||
|
|
|
@ -77,11 +77,12 @@ define internal dso_local i32 @f1(i64 %a) norecurse {
|
|||
entry:
|
||||
; CHECK: %{{[0-9]+}} = llvm.inttoptr %arg0 : !llvm.i64 to !llvm<"i64*">
|
||||
%aa = inttoptr i64 %a to i64*
|
||||
; CHECK: %[[addrof:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
|
||||
; CHECK: %{{[0-9]+}} = llvm.ptrtoint %[[addrof]] : !llvm<"double*"> to !llvm.i64
|
||||
; %[[addrof:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
|
||||
; %[[addrof2:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
|
||||
; %{{[0-9]+}} = llvm.inttoptr %arg0 : !llvm.i64 to !llvm<"i64*">
|
||||
; %{{[0-9]+}} = llvm.ptrtoint %[[addrof2]] : !llvm<"double*"> to !llvm.i64
|
||||
; %{{[0-9]+}} = llvm.getelementptr %[[addrof]][%3] : (!llvm<"double*">, !llvm.i32) -> !llvm<"double*">
|
||||
%bb = ptrtoint double* @g2 to i64
|
||||
; CHECK-DAG: %[[addrof2:[0-9]+]] = llvm.mlir.addressof @g2 : !llvm<"double*">
|
||||
; CHECK: %{{[0-9]+}} = llvm.getelementptr %[[addrof2]][%[[c2]]] : (!llvm<"double*">, !llvm.i32) -> !llvm<"double*">
|
||||
%cc = getelementptr double, double* @g2, i32 2
|
||||
; CHECK: %[[b:[0-9]+]] = llvm.trunc %arg0 : !llvm.i64 to !llvm.i32
|
||||
%b = trunc i64 %a to i32
|
||||
|
@ -260,3 +261,39 @@ define i32 @postcaller() {
|
|||
%3 = call i32 %2()
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
@_ZTIi = external dso_local constant i8*
|
||||
@_ZTIii= external dso_local constant i8**
|
||||
declare void @foo(i8*)
|
||||
declare i8* @bar(i8*)
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
|
||||
; CHECK-LABEL: @invokeLandingpad
|
||||
define i32 @invokeLandingpad() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
; CHECK: %[[a1:[0-9]+]] = llvm.bitcast %{{[0-9]+}} : !llvm<"i8***"> to !llvm<"i8*">
|
||||
; CHECK: %[[a3:[0-9]+]] = llvm.alloca %{{[0-9]+}} x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*">
|
||||
%1 = alloca i8
|
||||
; CHECK: llvm.invoke @foo(%[[a3]]) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> ()
|
||||
invoke void @foo(i8* %1) to label %4 unwind label %2
|
||||
|
||||
; CHECK: ^bb1:
|
||||
; CHECK: %{{[0-9]+}} = llvm.landingpad (catch %{{[0-9]+}} : !llvm<"i8**">) (catch %[[a1]] : !llvm<"i8*">) (filter %{{[0-9]+}} : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
|
||||
%3 = landingpad { i8*, i32 } catch i8** @_ZTIi catch i8* bitcast (i8*** @_ZTIii to i8*)
|
||||
; FIXME: Change filter to a constant array once they are handled.
|
||||
; Currently, even though it parses this, LLVM module is broken
|
||||
filter [1 x i8] [i8 1]
|
||||
; CHECK: llvm.br ^bb3
|
||||
br label %5
|
||||
|
||||
; CHECK: ^bb2:
|
||||
; CHECK: llvm.return %{{[0-9]+}} : !llvm.i32
|
||||
ret i32 1
|
||||
|
||||
; CHECK: ^bb3:
|
||||
; CHECK: %{{[0-9]+}} = llvm.invoke @bar(%[[a3]]) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> !llvm<"i8*">
|
||||
%6 = invoke i8* @bar(i8* %1) to label %4 unwind label %2
|
||||
|
||||
; CHECK: ^bb4:
|
||||
; CHECK: llvm.return %{{[0-9]+}} : !llvm.i32
|
||||
ret i32 0
|
||||
}
|
||||
|
|
|
@ -1130,3 +1130,44 @@ llvm.func @cmpxchg(%ptr : !llvm<"float*">, %cmp : !llvm.float, %val: !llvm.float
|
|||
%2 = llvm.extractvalue %0[1] : !llvm<"{ float, i1 }">
|
||||
llvm.return
|
||||
}
|
||||
|
||||
llvm.mlir.global external constant @_ZTIi() : !llvm<"i8*">
|
||||
llvm.func @foo(!llvm<"i8*">)
|
||||
llvm.func @bar(!llvm<"i8*">) -> !llvm<"i8*">
|
||||
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
|
||||
|
||||
// CHECK-LABEL: @invokeLandingpad
|
||||
llvm.func @invokeLandingpad() -> !llvm.i32 {
|
||||
// CHECK: %[[a1:[0-9]+]] = alloca i8
|
||||
%0 = llvm.mlir.constant(0 : i32) : !llvm.i32
|
||||
%1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]">
|
||||
%2 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
|
||||
%3 = llvm.bitcast %2 : !llvm<"i8**"> to !llvm<"i8*">
|
||||
%4 = llvm.mlir.null : !llvm<"i8**">
|
||||
%5 = llvm.mlir.constant(1 : i32) : !llvm.i32
|
||||
%6 = llvm.alloca %5 x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*">
|
||||
// CHECK: invoke void @foo(i8* %[[a1]])
|
||||
// CHECK-NEXT: to label %[[normal:[0-9]+]] unwind label %[[unwind:[0-9]+]]
|
||||
llvm.invoke @foo(%6) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> ()
|
||||
|
||||
// CHECK: [[unwind]]:
|
||||
^bb1:
|
||||
// CHECK: %{{[0-9]+}} = landingpad { i8*, i32 }
|
||||
// CHECK-NEXT: catch i8** null
|
||||
// CHECK-NEXT: catch i8* bitcast (i8** @_ZTIi to i8*)
|
||||
// CHECK-NEXT: filter [1 x i8] c"\01"
|
||||
%7 = llvm.landingpad (catch %4 : !llvm<"i8**">) (catch %3 : !llvm<"i8*">) (filter %1 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
|
||||
// CHECK: br label %[[final:[0-9]+]]
|
||||
llvm.br ^bb3
|
||||
|
||||
// CHECK: [[normal]]:
|
||||
// CHECK-NEXT: ret i32 1
|
||||
^bb2: // 2 preds: ^bb0, ^bb3
|
||||
llvm.return %5 : !llvm.i32
|
||||
|
||||
// CHECK: [[final]]:
|
||||
// CHECK-NEXT: %{{[0-9]+}} = invoke i8* @bar(i8* %[[a1]])
|
||||
// CHECK-NEXT: to label %[[normal]] unwind label %[[unwind]]
|
||||
^bb3: // pred: ^bb1
|
||||
%8 = llvm.invoke @bar(%6) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> !llvm<"i8*">
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue