From d242aa245ccfaa527d27c75e50f9b73223aec14b Mon Sep 17 00:00:00 2001 From: Shraiysh Vaishay Date: Thu, 30 Jan 2020 12:50:12 +0100 Subject: [PATCH] [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 (https://github.com/llvm/llvm-project/blob/4f82af81a04d711721300f6ca32f402f2ea6faf4/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 Differential Revision: https://reviews.llvm.org/D72006 --- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 35 +++++ mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 225 +++++++++++++++++++++++++++ mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp | 68 ++++++-- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 28 ++++ mlir/test/Dialect/LLVMIR/invalid.mlir | 52 +++++++ mlir/test/Dialect/LLVMIR/roundtrip.mlir | 50 ++++++ mlir/test/Target/import.ll | 45 +++++- mlir/test/Target/llvmir.mlir | 41 +++++ 8 files changed, 525 insertions(+), 19 deletions(-) diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index e73e941..6b74ae3 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -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:$callee, + Variadic)>, + Results<(outs Variadic)> { + let builders = [OpBuilder< + "Builder *b, OperationState &result, ArrayRef 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 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)> { + 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:$callee, Variadic)>, diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index a49df63..b0bfbfc 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -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(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 argTypes( + llvm::drop_begin(op.getOperandTypes(), isDirect ? 0 : 1)); + + p << " : " + << FunctionType::get(argTypes, op.getResultTypes(), 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 funcType; + 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 && 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::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 (Type ty : funcType.getInputs()) { + if (auto argType = ty.dyn_cast()) + 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().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(value.getDefiningOp())) { + if (auto addrOp = + dyn_cast_or_null(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(value.getDefiningOp())) + continue; + if (dyn_cast_or_null(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().isArrayTy(); + p << '(' << (isArrayTy ? "filter " : "catch ") << value << " : " + << value.getType() << ") "; + } + + 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) { + // 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. //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp index 0692b9b..7e2b9f9 100644 --- a/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ b/mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -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 &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(unknownLoc, type, attr); + return instMap[c] = bEntry.create(unknownLoc, type, attr); } if (auto *cn = dyn_cast(c)) { - OpBuilder b(currentEntryBlock, currentEntryBlock->begin()); LLVMType type = processType(cn->getType()); if (!type) return nullptr; - return instMap[c] = b.create(unknownLoc, type); + return instMap[c] = bEntry.create(unknownLoc, type); } + if (auto *GV = dyn_cast(c)) + return bEntry.create(UnknownLoc::get(context), + processGlobal(GV), + ArrayRef()); + if (auto *ce = dyn_cast(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(value)) { - auto global = processGlobal(GV); - if (!global) - return nullptr; - return b.create(UnknownLoc::get(context), global, - ArrayRef()); - } - - // Note, constant global variables are both GlobalVariables and Constants, - // so we handle GlobalVariables first above. if (auto *c = dyn_cast(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 &blockArguments) { for (auto inst = target->begin(); isa(inst); ++inst) { auto *PN = cast(&*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(inst); + SmallVector ops; + + for (unsigned i = 0, ie = lpi->getNumClauses(); i < ie; i++) + ops.push_back(processConstant(lpi->getClause(i))); + + b.create(loc, processType(lpi->getType()), lpi->isCleanup(), + ops); + return success(); + } + case llvm::Instruction::Invoke: { + llvm::InvokeInst *ii = cast(inst); + + SmallVector tys; + if (!ii->getType()->isVoidTy()) + tys.push_back(processType(inst->getType())); + + SmallVector ops; + ops.reserve(inst->getNumOperands() + 1); + for (auto &op : ii->arg_operands()) + ops.push_back(processValue(op.get())); + + SmallVector normalArgs, unwindArgs; + processBranchArgs(ii, ii->getNormalDest(), normalArgs); + processBranchArgs(ii, ii->getUnwindDest(), unwindArgs); + + Operation *op; + if (llvm::Function *callee = ii->getCalledFunction()) { + op = b.create(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(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(inst); diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index b5839cd..07600bc 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -307,6 +307,34 @@ LogicalResult ModuleTranslation::convertOperation(Operation &opInst, 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)) { + llvm::Type *ty = lpOp.getType().dyn_cast().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(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(opInst)) { diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index 0de11fe..153248d 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -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 +} diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index 3b371a0..53a2bb1 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -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 +} diff --git a/mlir/test/Target/import.ll b/mlir/test/Target/import.ll index ace9154..9c1f13a 100644 --- a/mlir/test/Target/import.ll +++ b/mlir/test/Target/import.ll @@ -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 +} diff --git a/mlir/test/Target/llvmir.mlir b/mlir/test/Target/llvmir.mlir index 5a9bfcf..5b426d7 100644 --- a/mlir/test/Target/llvmir.mlir +++ b/mlir/test/Target/llvmir.mlir @@ -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*"> +} -- 2.7.4