Index: mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td =================================================================== --- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -323,6 +323,58 @@ def LLVM_FPTruncOp : LLVM_CastOp<"fptrunc", "CreateFPTrunc">; // Call-related operations. +def LLVM_InvokeOp : LLVM_Op<"invoke", [Terminator]>, + Arguments<(ins OptionalAttr:$callee, + Variadic)>, + Results<(outs Variadic)>, + LLVM_TwoBuilders { + let verifier = [{ + if(getNumResults() > 1) + return emitOpError("must have 0 or 1 result"); + if(getNumSuccessors() != 2) + return emitOpError("must have normal and unwind destinations"); + + // If the first instruction is not LandingpadOp + SmallVector prevInsts; + LandingpadOp lpOp; + + // Get landindpad instruction and all instructions before it + for(auto &inst : *getSuccessor(1)) { + if((lpOp = dyn_cast(inst))) + break; + prevInsts.push_back(&inst); + } + + if(!lpOp) { + return emitOpError("No landingpad instruction in unwind branch"); + } + + // Check that all those previous instructions are operands for landingpad + for(auto prevInst : prevInsts) { + bool flag = false; + // Check it must be an operand + for(auto ops : lpOp.getOperands()) + if(ops.getDefiningOp() == prevInst) + flag = true; + + if(!flag) + return emitOpError("first instruction in unwind branch should be" + "landingpad instruction"); + } + return success(); + }]; + let parser = [{ return parseInvokeOp(parser, result); }]; + let printer = [{ printInvokeOp(p, *this); }]; +} + +def LLVM_LandingpadOp : LLVM_OneResultOp<"landingpad">, + Arguments<(ins BoolAttr:$cleanup, + Variadic)> { + let parser = [{ return parseLandingpadOp(parser, result); }]; + let printer = [{ printLandingpadOp(p, *this); }]; +} + def LLVM_CallOp : LLVM_Op<"call">, Arguments<(ins OptionalAttr:$callee, Variadic)>, Index: mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp =================================================================== --- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -286,6 +286,197 @@ return success(); } +//===----------------------------------------------------------------------===// +// Printing/parsing for LLVM::InvokeOp. +//===----------------------------------------------------------------------===// + +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 resultTypes(op.getResultTypes()); + SmallVector argTypes( + llvm::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1)); + + p << " : " << FunctionType::get(argTypes, resultTypes, op.getContext()); +} + +// ::= `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 operands; + FunctionType type; + SymbolRefAttr funcAttr; + llvm::SMLoc trailingTypeLoc; + Block *normalDest, *unwindDest; + SmallVector 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) + if (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(type)) + return failure(); + + auto funcType = type.dyn_cast(); + if (!funcType) + return parser.emitError(trailingTypeLoc, "expected function type"); + 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::LLVMType llvmResultType; + if (funcType.getNumResults() == 0) { + llvmResultType = LLVM::LLVMType::getVoidTy(llvmDialect); + } else { + llvmResultType = funcType.getResult(0).dyn_cast(); + if (!llvmResultType) + return parser.emitError(trailingTypeLoc, + "expected result to have LLVM type"); + } + + SmallVector argTypes; + argTypes.reserve(funcType.getNumInputs()); + for (int i = 0, e = funcType.getNumInputs(); i < e; ++i) { + auto argType = funcType.getInput(i).dyn_cast(); + if (!argType) + return parser.emitError(trailingTypeLoc, + "expected LLVM types as inputs"); + argTypes.push_back(argType); + } + auto llvmFuncType = LLVM::LLVMType::getFunctionTy(llvmResultType, argTypes, + /*isVarArg=*/false); + auto wrappedFuncType = llvmFuncType.getPointerTo(); + + auto funcArguments = + ArrayRef(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(); +} + +//===----------------------------------------------------------------------===// +// Printing/parsing for LLVM::LandingpadOp. +//===----------------------------------------------------------------------===// + +static void printLandingpadOp(OpAsmPrinter &p, LandingpadOp &op) { + p << op.getOperationName() << ' '; + + // Check for cleanup + if (op.cleanup()) { + p << "cleanup "; + } + + // Clauses + for (auto it : op.getOperands()) { + // Similar to llvm - if clause is an array type then it is filter + // clause else catch clause + bool isArrayTy = it.getType().cast().isArrayTy(); + p << (isArrayTy ? "filter " : "catch ") << it.getType() << ' ' << it << ' '; + } + + p.printOptionalAttrDict(op.getAttrs(), {"cleanup"}); + + p << ": " << op.getType(); +} + +// ::= `llvm.landingpad` `cleanup`? +// ((`catch` | `filter`) operand-type ssa-use)* attribute-dict? +static ParseResult parseLandingpadOp(OpAsmParser &parser, + OperationState &result) { + + Type type; + llvm::SMLoc typeLocation; + + // Check for cleanup + if (parser.parseOptionalKeyword("cleanup")) + result.addAttribute("cleanup", parser.getBuilder().getBoolAttr(false)); + else + result.addAttribute("cleanup", parser.getBuilder().getBoolAttr(true)); + + // Parse clauses with types + SmallVector operands; + SmallVector tys; + while (!parser.parseOptionalKeyword("filter") || + !parser.parseOptionalKeyword("catch")) { + OpAsmParser::OperandType operand; + Type ty; + if (parser.parseType(ty) || parser.parseOperand(operand)) + return failure(); + tys.push_back(ty); + operands.push_back(operand); + } + + // Resolve clauses and insert them in result + if (parser.resolveOperands(operands, tys, parser.getCurrentLocation(), + result.operands)) + return failure(); + + parser.getCurrentLocation(&typeLocation); + if (parser.parseColon() || parser.parseType(type)) { + return parser.emitError(typeLocation, "invalid type"); + } + + result.addTypes(type); + return success(); +} + //===----------------------------------------------------------------------===// // Printing/parsing for LLVM::CallOp. //===----------------------------------------------------------------------===// Index: mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -76,7 +76,7 @@ /// `br` branches to `target`. Return the block arguments to attach to the /// generated branch op. These should be in the same order as the PHIs in /// `target`. - SmallVector processBranchArgs(llvm::BranchInst *br, + SmallVector processBranchArgs(llvm::Instruction *br, llvm::BasicBlock *target); /// Return `value` as an attribute to attach to a GlobalOp. Attribute getConstantAsAttr(llvm::Constant *value); @@ -406,7 +406,7 @@ // `br` branches to `target`. Return the branch arguments to `br`, in the // same order of the PHIs in `target`. -SmallVector Importer::processBranchArgs(llvm::BranchInst *br, +SmallVector Importer::processBranchArgs(llvm::Instruction *br, llvm::BasicBlock *target) { SmallVector v; for (auto inst = target->begin(); isa(inst); ++inst) { @@ -519,6 +519,51 @@ v = op->getResult(0); return success(); } + case llvm::Instruction::LandingPad: { + llvm::LandingPadInst *lpi = cast(inst); + SmallVector ops; + for (unsigned i = 0; i < lpi->getNumClauses(); i++) { + ops.push_back(processValue(lpi->getClause(i))); + } + + b.create(loc, processType(lpi->getType()), + lpi->isCleanup(), ops); + return success(); + } + case llvm::Instruction::Invoke: { + llvm::InvokeInst *ii = cast(inst); + + OperationState opState(loc, "llvm.invoke"); + + SmallVector tys; + if (!ii->getType()->isVoidTy()) + tys.push_back(processType(inst->getType())); + opState.addTypes(tys); + + SmallVector ops; + ops.reserve(inst->getNumOperands() + 1); + if (llvm::Function *callee = ii->getCalledFunction()) { + opState.addAttribute(b.getIdentifier("callee"), + b.getSymbolRefAttr(callee->getName())); + } else { + ops.push_back(processValue(ii->getCalledValue())); + } + for (auto &op : ii->arg_operands()) + ops.push_back(processValue(op.get())); + + opState.addOperands(ops); + + opState.addSuccessor(blocks[ii->getNormalDest()], + processBranchArgs(ii, ii->getNormalDest())); + opState.addSuccessor(blocks[ii->getUnwindDest()], + processBranchArgs(ii, ii->getUnwindDest())); + + Operation *op = b.createOperation(opState); + + if (!ii->getType()->isVoidTy()) + v = op->getResult(0); + return success(); + } case llvm::Instruction::GetElementPtr: { // FIXME: Support inbounds GEPs. llvm::GetElementPtrInst *gep = cast(inst); Index: mlir/lib/Target/LLVMIR/ModuleTranslation.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -201,6 +201,44 @@ return success(result->getType()->isVoidTy()); } + if (auto invOp = dyn_cast(opInst)) { + auto operands = lookupValues(opInst.getOperands()); + ArrayRef operandsRef(operands); + if (auto attr = opInst.getAttrOfType("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(opInst)) { + Type *lpType; + + // Only one type - that is going to be lpType + for (auto res : opInst.getResultTypes()) { + lpType = &res; + } + + llvm::Type *ty = lpType->dyn_cast().getUnderlyingType(); + llvm::LandingPadInst *lpi = + builder.CreateLandingPad(ty, lpOp.getNumOperands()); + + // Add clauses + auto operands = lookupValues(lpOp.getOperands()); + for (auto operand : operands) { + if (auto constOper = dyn_cast(operand)) + lpi->addClause(constOper); + else + return emitError(opInst.getLoc(), "Constant clauses expected"); + } + 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(opInst)) { Index: mlir/test/Dialect/LLVMIR/roundtrip.mlir =================================================================== --- mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -218,3 +218,53 @@ %1 = llvm.mlir.null : !llvm<"{void(i32, void()*)*, i64}*"> 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: %0 = llvm.mlir.constant(0 : i32) : !llvm.i32 +// CHECK-NEXT: %1 = llvm.mlir.constant(3 : i32) : !llvm.i32 +// CHECK-NEXT: %2 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]"> +// CHECK-NEXT: %3 = llvm.mlir.null : !llvm<"i8**"> +// CHECK-NEXT: %4 = llvm.mlir.null : !llvm<"i8*"> +// CHECK-NEXT: %5 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> +// CHECK-NEXT: %6 = llvm.bitcast %5 : !llvm<"i8**"> to !llvm<"i8*"> +// CHECK-NEXT: %7 = llvm.mlir.constant(1 : i32) : !llvm.i32 +// CHECK-NEXT: %8 = llvm.alloca %7 x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*"> +// CHECK-NEXT: %9 = llvm.invoke @foo(%7) 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: %10 = llvm.landingpad catch !llvm<"i8**"> %3 catch !llvm<"i8*"> %6 filter !llvm<"[1 x i8]"> %2 : !llvm<"{ i8*, i32 }"> +// CHECK-NEXT: llvm.br ^bb3 +^bb1: + %10 = llvm.landingpad catch !llvm<"i8**"> %3 catch !llvm<"i8*"> %6 filter !llvm<"[1 x i8]"> %2 : !llvm<"{ i8*, i32 }"> + llvm.br ^bb3 + +// CHECK-NEXT: ^bb2: +// CHECK-NEXT: llvm.return %7 : !llvm.i32 +^bb2: + llvm.return %7 : !llvm.i32 + +// CHECK-NEXT: ^bb3: +// CHECK-NEXT: llvm.invoke @bar(%8, %6, %4) 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 %0 : !llvm.i32 +^bb4: + llvm.return %0 : !llvm.i32 +} \ No newline at end of file Index: mlir/test/Target/import.ll =================================================================== --- mlir/test/Target/import.ll +++ mlir/test/Target/import.ll @@ -245,3 +245,40 @@ %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 %[[t1:[0-9]+]] : !llvm<"i8***"> to !llvm<"i8*"> + ; CHECK: %[[a3:[0-9]+]] = llvm.alloca %[[t2:[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: %6 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> + ; CHECK: %[[t3:[0-9]+]] = llvm.landingpad catch !llvm<"i8**"> %6 catch !llvm<"i8*"> %[[a1]] filter !llvm<"[1 x i8]"> %[[t4:[0-9]+]] : !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 %[[t5:[0-9]+]] : !llvm.i32 + ret i32 1 + +; CHECK: ^bb3: + ; CHECK: %[[a4:[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 %[[t6:[0-9]+]] : !llvm.i32 + ret i32 0 +} \ No newline at end of file Index: mlir/test/Target/llvmir.mlir =================================================================== --- mlir/test/Target/llvmir.mlir +++ mlir/test/Target/llvmir.mlir @@ -1067,3 +1067,44 @@ // CHECK: ret i32* null llvm.return %0 : !llvm<"i32*"> } + +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: %[[a3:[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 !llvm<"i8**"> %4 catch !llvm<"i8*"> %3 filter !llvm<"[1 x i8]"> %1 : !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: %[[t1:[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*"> +}