diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -1293,28 +1293,73 @@ if (failed(convertCallTypeAndOperands(invokeInst, types, operands))) return failure(); - SmallVector normalArgs, unwindArgs; - (void)convertBranchArgs(invokeInst, invokeInst->getNormalDest(), - normalArgs); - (void)convertBranchArgs(invokeInst, invokeInst->getUnwindDest(), - unwindArgs); + // Check whether the invoke result is an argument to the normal destination + // block. + bool invokeResultUsedInPhi = + llvm::any_of(invokeInst->getNormalDest()->phis(), [&](const auto &phi) { + return phi.getIncomingValueForBlock(invokeInst->getParent()) == + invokeInst; + }); + + auto *normalDest = lookupBlock(invokeInst->getNormalDest()); + auto *directNormalDest = normalDest; + if (invokeResultUsedInPhi) { + // The invoke result cannot be an argument to the normal destination + // block, as that would imply using the invoke operation result in its + // definition, so we need to create a dummy block to serve as an + // intermediate destination. + OpBuilder::InsertionGuard g(builder); + directNormalDest = builder.createBlock(normalDest); + } + + SmallVector unwindArgs; + if (failed(convertBranchArgs(invokeInst, invokeInst->getUnwindDest(), + unwindArgs))) + return failure(); + // Create the invoke operation. Normal destination block arguments will be + // added later on to handle the case in which the operation result is + // included in this list. InvokeOp invokeOp; if (llvm::Function *callee = invokeInst->getCalledFunction()) { invokeOp = builder.create( loc, types, SymbolRefAttr::get(builder.getContext(), callee->getName()), operands, - lookupBlock(invokeInst->getNormalDest()), normalArgs, + directNormalDest, ValueRange(), lookupBlock(invokeInst->getUnwindDest()), unwindArgs); } else { invokeOp = builder.create( - loc, types, operands, lookupBlock(invokeInst->getNormalDest()), - normalArgs, lookupBlock(invokeInst->getUnwindDest()), unwindArgs); + loc, types, operands, directNormalDest, ValueRange(), + lookupBlock(invokeInst->getUnwindDest()), unwindArgs); } if (!invokeInst->getType()->isVoidTy()) mapValue(inst, invokeOp.getResults().front()); else mapNoResultOp(inst, invokeOp); + + SmallVector normalArgs; + if (failed(convertBranchArgs(invokeInst, invokeInst->getNormalDest(), + normalArgs))) + return failure(); + + if (invokeResultUsedInPhi) { + // The dummy normal dest block will just host an unconditional branch + // instruction to the normal destination block passing the required block + // arguments (including the invoke operation's result). + OpBuilder::InsertionGuard g(builder); + builder.setInsertionPointToStart(directNormalDest); + builder.create(loc, normalArgs, normalDest); + } else { + // If the invoke operation's result is not a block argument to the normal + // destination block, just add the block arguments as usual. + assert(llvm::none_of( + normalArgs, + [&](auto val) { return val.getDefiningOp() == invokeOp; }) && + "An llvm.invoke operation cannot pass its result as a block " + "argument."); + invokeOp.getNormalDestOperandsMutable().append(normalArgs); + } + return success(); } if (inst->getOpcode() == llvm::Instruction::GetElementPtr) { diff --git a/mlir/test/Target/LLVMIR/Import/basic.ll b/mlir/test/Target/LLVMIR/Import/basic.ll --- a/mlir/test/Target/LLVMIR/Import/basic.ll +++ b/mlir/test/Target/LLVMIR/Import/basic.ll @@ -87,6 +87,77 @@ ret i32 0 } +declare i32 @foo2() + +; CHECK-LABEL: @invokePhi +; CHECK-SAME: (%[[cond:.*]]: i1) -> i32 +define i32 @invokePhi(i1 %cond) personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: %[[c0:.*]] = llvm.mlir.constant(0 : i32) : i32 + ; CHECK: llvm.cond_br %[[cond:.*]], ^bb1, ^bb2 + br i1 %cond, label %call, label %nocall +; CHECK: ^bb1: // pred: ^bb0 +call: + ; CHECK: %[[invoke:.*]] = llvm.invoke @foo2() to ^bb3 unwind ^bb5 : () -> i32 + %invoke = invoke i32 @foo2() to label %bb0 unwind label %bb1 +; CHECK: ^bb2: // pred: ^bb0 +nocall: + ; CHECK: llvm.br ^bb4(%[[c0]] : i32) + br label %bb0 +; CHECK: ^bb3: // pred: ^bb1 + ; CHECK: llvm.br ^bb4(%[[invoke]] : i32) +; CHECK: ^bb4(%[[barg:.*]]: i32): // 2 preds: ^bb2, ^bb3 +bb0: + %ret = phi i32 [ 0, %nocall ], [ %invoke, %call ] + ; CHECK: llvm.return %[[barg]] : i32 + ret i32 %ret +; CHECK: ^bb5: // pred: ^bb1 +bb1: + ; CHECK: %[[lp:.*]] = llvm.landingpad cleanup : i32 + %resume = landingpad i32 cleanup + ; CHECK: llvm.resume %[[lp]] : i32 + resume i32 %resume +} + +; CHECK-LABEL: @invokePhiComplex +; CHECK-SAME: (%[[cond:.*]]: i1) -> i32 +define i32 @invokePhiComplex(i1 %cond) personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: %[[c0:.*]] = llvm.mlir.constant(0 : i32) : i32 + ; CHECK: %[[c1:.*]] = llvm.mlir.constant(1 : i32) : i32 + ; CHECK: %[[c2:.*]] = llvm.mlir.constant(2 : i32) : i32 + ; CHECK: %[[c20:.*]] = llvm.mlir.constant(20 : i32) : i32 + ; CHECK: llvm.cond_br %[[cond]], ^bb1, ^bb2 + br i1 %cond, label %call, label %nocall +; CHECK: ^bb1: // pred: ^bb0 +call: + ; CHECK: %[[invoke:.*]] = llvm.invoke @foo2() to ^bb3 unwind ^bb5 : () -> i32 + %invoke = invoke i32 @foo2() to label %bb0 unwind label %bb1 +; CHECK: ^bb2: // pred: ^bb0 +nocall: + ; CHECK: llvm.br ^bb4(%[[c0]], %[[c1]], %[[c2]] : i32, i32, i32) + br label %bb0 +; CHECK: ^bb3: // pred: ^bb1 + ; CHECK: llvm.br ^bb4(%[[invoke]], %[[c20]], %[[invoke]] : i32, i32, i32) +; CHECK: ^bb4(%[[barg0:.*]]: i32, %[[barg1:.*]]: i32, %[[barg2:.*]]: i32): // 2 preds: ^bb2, ^bb3 +bb0: + %a = phi i32 [ 0, %nocall ], [ %invoke, %call ] + %b = phi i32 [ 1, %nocall ], [ 20, %call ] + %c = phi i32 [ 2, %nocall ], [ %invoke, %call ] + ; CHECK: %[[add0:.*]] = llvm.add %[[barg0]], %[[barg1]] : i32 + ; CHECK: %[[add1:.*]] = llvm.add %[[barg2]], %[[add0]] : i32 + %d = add i32 %a, %b + %e = add i32 %c, %d + ; CHECK: llvm.return %[[add1]] : i32 + ret i32 %e +; CHECK: ^bb5: // pred: ^bb1 +bb1: + ; CHECK: %[[lp:.*]] = llvm.landingpad cleanup : i32 + %resume = landingpad i32 cleanup + ; CHECK: llvm.resume %[[lp]] : i32 + resume i32 %resume +} + ; CHECK-LABEL: @hasGCFunction ; CHECK-SAME: garbageCollector = "statepoint-example" define void @hasGCFunction() gc "statepoint-example" {