Index: mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td =================================================================== --- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -480,6 +480,21 @@ let parser = [{ return parseReturnOp(parser, result); }]; let printer = [{ printReturnOp(p, *this); }]; } +def LLVM_ResumeOp : LLVM_TerminatorOp<"resume", []> { + string llvmBuilder = [{ builder.CreateResume($args[0]); }]; + + let verifier = [{ + if(getNumOperands() != 1) + return emitOpError("expects 1 operand"); + if(!isa(getOperand(0).getDefiningOp())) + return emitOpError("expects landingpad value as operand"); + // No check for personality of function - landingpad op verifies it + return success(); + }]; + + let parser = [{ return parseResumeOp(parser, result); }]; + let printer = [{ printResumeOp(p, *this); }]; +} def LLVM_UnreachableOp : LLVM_TerminatorOp<"unreachable", []> { string llvmBuilder = [{ builder.CreateUnreachable(); }]; let parser = [{ return success(); }]; @@ -620,7 +635,8 @@ def LLVM_LLVMFuncOp : LLVM_ZeroResultOp<"func", [IsolatedFromAbove, FunctionLike, Symbol]>, Arguments<(ins DefaultValuedAttr:$linkage)> { + "Linkage::External">:$linkage, + OptionalAttr:$personality)> { let summary = "LLVM dialect function, has wrapped LLVM IR function type"; let regions = (region AnyRegion:$body); @@ -628,6 +644,16 @@ let skipDefaultBuilders = 1; let builders = [ + OpBuilder<"Builder *builder, OperationState &result, StringRef name, " + "LLVMType type, FlatSymbolRefAttr personality, " + "LLVM::Linkage linkage = LLVM::Linkage::External, " + "ArrayRef attrs = {}, " + "ArrayRef argAttrs = {}", + [{ + result.addAttribute("personality", personality); + build(builder, result, name, type, linkage, attrs, argAttrs); + }] + >, OpBuilder<"Builder *builder, OperationState &result, StringRef name, " "LLVMType type, LLVM::Linkage linkage = LLVM::Linkage::External, " "ArrayRef attrs = {}, " Index: mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp =================================================================== --- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -373,6 +373,12 @@ static LogicalResult verify(LandingpadOp op) { Value value; + if (LLVMFuncOp func = op.getParentOfType()) { + if (!func.personality().hasValue()) + return op.emitError( + "llvm.landingpad needs to be in a function with a personality"); + } + if (!op.cleanup() && op.getOperands().empty()) return op.emitError("landingpad instruction expects at least one clause or " "cleanup attribute"); @@ -911,6 +917,31 @@ return success(); } +//===----------------------------------------------------------------------===// +// Printing/parsing for LLVM::ResumeOp. +//===----------------------------------------------------------------------===// + +static void printResumeOp(OpAsmPrinter &p, ResumeOp &op) { + p << op.getOperationName(); + p.printOptionalAttrDict(op.getAttrs()); + assert(op.getNumOperands() == 1); + p << ' ' << op.getOperand(0) << " : " << op.getOperand(0).getType(); +} + +// ::= `llvm.resume` ssa-use-list attribute-dict? `:` +// type-list-no-parens +static ParseResult parseResumeOp(OpAsmParser &parser, OperationState &result) { + Type type; + OpAsmParser::OperandType operand; + if (parser.parseOperand(operand) || + parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(type) || + parser.resolveOperand(operand, type, result.operands)) + return failure(); + + return success(); +} + //===----------------------------------------------------------------------===// // Verifier for LLVM::AddressOfOp. //===----------------------------------------------------------------------===// Index: mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp +++ mlir/lib/Target/LLVMIR/ConvertFromLLVMIR.cpp @@ -465,7 +465,7 @@ // FIXME: switch // FIXME: indirectbr // FIXME: invoke - // FIXME: resume + INST(Resume, Resume), // FIXME: unreachable // FIXME: cleanupret // FIXME: catchret @@ -579,6 +579,7 @@ case llvm::Instruction::Load: case llvm::Instruction::Store: case llvm::Instruction::Ret: + case llvm::Instruction::Resume: case llvm::Instruction::Trunc: case llvm::Instruction::ZExt: case llvm::Instruction::SExt: @@ -692,8 +693,12 @@ 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); + Type ty = processType(lpi->getType()); + if (!ty) + return failure(); + + Operation *op = b.create(loc, ty, lpi->isCleanup(), ops); + v = op->getResult(0); return success(); } case llvm::Instruction::Invoke: { @@ -757,8 +762,29 @@ return failure(); b.setInsertionPoint(module.getBody(), getFuncInsertPt()); - LLVMFuncOp fop = b.create(UnknownLoc::get(context), f->getName(), - functionType); + LLVMFuncOp fop; + if (f->hasPersonalityFn()) { + FlatSymbolRefAttr personality; + llvm::Constant *pf = f->getPersonalityFn(); + + // Trying to deduce personality - first directly + if (pf->hasName()) + personality = b.getSymbolRefAttr(pf->getName()); + + if (personality) { + fop = b.create(UnknownLoc::get(context), f->getName(), + functionType, personality); + } else { + emitWarning(UnknownLoc::get(context), + "could not deduce personality, skipping it"); + fop = b.create(UnknownLoc::get(context), f->getName(), + functionType); + } + } else { + fop = b.create(UnknownLoc::get(context), f->getName(), + functionType); + } + if (f->isDeclaration()) return success(); Index: mlir/lib/Target/LLVMIR/ModuleTranslation.cpp =================================================================== --- mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -334,6 +334,7 @@ if (auto constOperand = dyn_cast(operand)) lpi->addClause(constOperand); } + valueMapping[lpOp.getResult()] = lpi; return success(); } @@ -541,6 +542,11 @@ argIdx++; } + // Setting personality + if (func.personality().hasValue()) + llvmFunc->setPersonalityFn( + functionMapping.lookup(func.personality().getValue())); + // First, create all blocks so we can jump to them. llvm::LLVMContext &llvmContext = llvmFunc->getContext(); for (auto &bb : func) { @@ -602,8 +608,11 @@ ModuleTranslation::lookupValues(ValueRange values) { SmallVector remapped; remapped.reserve(values.size()); - for (Value v : values) + for (Value v : values) { + assert(valueMapping.find(v) != valueMapping.end() && + "referencing undefined value"); remapped.push_back(valueMapping.lookup(v)); + } return remapped; } Index: mlir/test/Dialect/LLVMIR/invalid.mlir =================================================================== --- mlir/test/Dialect/LLVMIR/invalid.mlir +++ mlir/test/Dialect/LLVMIR/invalid.mlir @@ -515,7 +515,7 @@ llvm.func @foo(!llvm.i32) -> !llvm.i32 llvm.func @__gxx_personality_v0(...) -> !llvm.i32 -llvm.func @bad_landingpad(%arg0: !llvm<"i8**">) { +llvm.func @bad_landingpad(%arg0: !llvm<"i8**">) attributes { personality = @__gxx_personality_v0} { %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 @@ -532,7 +532,7 @@ llvm.func @foo(!llvm.i32) -> !llvm.i32 llvm.func @__gxx_personality_v0(...) -> !llvm.i32 -llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 { +llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 attributes { personality = @__gxx_personality_v0} { %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}} @@ -551,7 +551,7 @@ llvm.func @foo(!llvm.i32) -> !llvm.i32 llvm.func @__gxx_personality_v0(...) -> !llvm.i32 -llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 { +llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 attributes { personality = @__gxx_personality_v0} { %0 = llvm.mlir.constant(1 : i32) : !llvm.i32 %1 = llvm.invoke @foo(%0) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32 ^bb1: // pred: ^bb0 @@ -561,3 +561,51 @@ %2 = llvm.landingpad : !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 attributes { personality = @__gxx_personality_v0 } { + %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 + %2 = llvm.landingpad cleanup : !llvm<"{ i8*, i32 }"> + // expected-error@+1 {{expected SSA operand}} + llvm.resume : !llvm<"{ i8*, i32 }"> +} + +// ----- + +llvm.func @foo(!llvm.i32) -> !llvm.i32 +llvm.func @__gxx_personality_v0(...) -> !llvm.i32 + +llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 attributes { personality = @__gxx_personality_v0 } { + %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 + %2 = llvm.landingpad cleanup : !llvm<"{ i8*, i32 }"> + // expected-error@+1 {{'llvm.resume' op expects landingpad value as operand}} + llvm.resume %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 {{llvm.landingpad needs to be in a function with a personality}} + %2 = llvm.landingpad cleanup : !llvm<"{ i8*, i32 }"> + llvm.resume %2 : !llvm<"{ i8*, i32 }"> +} \ No newline at end of file Index: mlir/test/Dialect/LLVMIR/roundtrip.mlir =================================================================== --- mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -238,7 +238,7 @@ llvm.func @__gxx_personality_v0(...) -> !llvm.i32 // CHECK-LABEL: @invokeLandingpad -llvm.func @invokeLandingpad() -> !llvm.i32 { +llvm.func @invokeLandingpad() -> !llvm.i32 attributes { personality = @__gxx_personality_v0} { // 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]"> @@ -261,11 +261,11 @@ %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 +// CHECK-NEXT: %[[lp:[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.resume %[[lp]] ^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 + llvm.resume %10 : !llvm<"{ i8*, i32 }"> // CHECK-NEXT: ^bb2: // CHECK-NEXT: llvm.return %[[a7]] : !llvm.i32 Index: mlir/test/Target/import.ll =================================================================== --- mlir/test/Target/import.ll +++ mlir/test/Target/import.ll @@ -269,7 +269,7 @@ declare i32 @__gxx_personality_v0(...) ; CHECK-LABEL: @invokeLandingpad -define i32 @invokeLandingpad() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +define i32 @invokeLandingpad() personality i32 (...)* @__gxx_personality_v0 { ; 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 @@ -277,14 +277,14 @@ 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 }"> + ; CHECK: %[[a4:[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: llvm.resume %[[a4]] : !llvm<"{ i8*, i32 }"> + resume { i8*, i32 } %3 + ; CHECK: ^bb2: ; CHECK: llvm.return %{{[0-9]+}} : !llvm.i32 ret i32 1 Index: mlir/test/Target/llvmir.mlir =================================================================== --- mlir/test/Target/llvmir.mlir +++ mlir/test/Target/llvmir.mlir @@ -1137,7 +1137,7 @@ llvm.func @__gxx_personality_v0(...) -> !llvm.i32 // CHECK-LABEL: @invokeLandingpad -llvm.func @invokeLandingpad() -> !llvm.i32 { +llvm.func @invokeLandingpad() -> !llvm.i32 attributes { personality = @__gxx_personality_v0} { // CHECK: %[[a1:[0-9]+]] = alloca i8 %0 = llvm.mlir.constant(0 : i32) : !llvm.i32 %1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]"> @@ -1152,18 +1152,18 @@ // CHECK: [[unwind]]: ^bb1: -// CHECK: %{{[0-9]+}} = landingpad { i8*, i32 } +// CHECK: %[[lp:[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: resume { i8*, i32 } %[[lp]] + llvm.resume %7 : !llvm<"{ i8*, i32 }"> // CHECK: [[normal]]: -// CHECK-NEXT: ret i32 1 +// CHECK-NEXT: br label %[[final:[0-9]+]] ^bb2: // 2 preds: ^bb0, ^bb3 - llvm.return %5 : !llvm.i32 + llvm.br ^bb3 // CHECK: [[final]]: // CHECK-NEXT: %{{[0-9]+}} = invoke i8* @bar(i8* %[[a1]])