[mlir] Added llvm.resume and personality functions in LLVM IR Dialect

`llvm.resume` is similar to `llvm.return` except that has to be exactly
one operand and that should be derived from a `llvm.landingpad`
instruction.  Any function having `llvm.landingpad` instruction must
have a personality attribute.

Example:
LLVM IR
```
define dso_local i32 @main() personality i32 (...)* @__gxx_personality_v0 {
  invoke void @foo(i32 42)
          to label %3 unwind label %1

1:                                                ; preds = %0
  %2 = landingpad i8*
          catch i8** @_ZTIi
          catch i8* bitcast (i8** @_ZTIi to i8*)
  resume i8* %2

3:                                                ; preds = %0
  ret i32 1
}
```

MLIR - LLVM IR Dialect

```
llvm.func @main() -> !llvm.i32 attributes {personality = @__gxx_personality_v0} {
    %0 = llvm.mlir.constant(1 : i32) : !llvm.i32
    %1 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
    %2 = llvm.bitcast %1 : !llvm<"i8**"> to !llvm<"i8*">
    %3 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
    %4 = llvm.mlir.constant(42 : i32) : !llvm.i32
    llvm.invoke @foo(%4) to ^bb2 unwind ^bb1 : (!llvm.i32) -> ()
  ^bb1:	// pred: ^bb0
    %5 = llvm.landingpad (catch %3 : !llvm<"i8**">) (catch %2 : !llvm<"i8*">) : !llvm<"i8*">
    llvm.resume %5 : !llvm<"i8*">
  ^bb2:	// pred: ^bb0
    llvm.return %0 : !llvm.i32
  }
```

Differential Revision: https://reviews.llvm.org/D71888
This commit is contained in:
Shraiysh Vaishay 2020-03-19 13:09:31 +01:00 committed by Alex Zinenko
parent 718d94187d
commit ff77397fcf
8 changed files with 111 additions and 16 deletions

View File

@ -506,6 +506,18 @@ def LLVM_ReturnOp : LLVM_TerminatorOp<"return", [NoSideEffect]>,
let parser = [{ return parseReturnOp(parser, result); }];
let printer = [{ printReturnOp(p, *this); }];
}
def LLVM_ResumeOp : LLVM_TerminatorOp<"resume", []> {
let arguments = (ins LLVM_Type:$value);
string llvmBuilder = [{ builder.CreateResume($value); }];
let verifier = [{
if (!isa_and_nonnull<LandingpadOp>(value().getDefiningOp()))
return emitOpError("expects landingpad value as operand");
// No check for personality of function - landingpad op verifies it.
return success();
}];
let assemblyFormat = "$value attr-dict `:` type($value)";
}
def LLVM_UnreachableOp : LLVM_TerminatorOp<"unreachable", []> {
string llvmBuilder = [{ builder.CreateUnreachable(); }];
let parser = [{ return success(); }];
@ -650,7 +662,8 @@ def LLVM_GlobalOp
def LLVM_LLVMFuncOp
: LLVM_ZeroResultOp<"func", [IsolatedFromAbove, FunctionLike, Symbol]>,
Arguments<(ins DefaultValuedAttr<Linkage,
"Linkage::External">:$linkage)> {
"Linkage::External">:$linkage,
OptionalAttr<FlatSymbolRefAttr>:$personality)> {
let summary = "LLVM dialect function, has wrapped LLVM IR function type";
let regions = (region AnyRegion:$body);

View File

@ -407,6 +407,11 @@ static ParseResult parseInvokeOp(OpAsmParser &parser, OperationState &result) {
static LogicalResult verify(LandingpadOp op) {
Value value;
if (LLVMFuncOp func = op.getParentOfType<LLVMFuncOp>()) {
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 "

View File

@ -60,6 +60,8 @@ public:
GlobalOp processGlobal(llvm::GlobalVariable *GV);
private:
/// Returns personality of `f` as a FlatSymbolRefAttr.
FlatSymbolRefAttr getPersonalityAsAttr(llvm::Function *f);
/// Imports `bb` into `block`, which must be initially empty.
LogicalResult processBasicBlock(llvm::BasicBlock *bb, Block *block);
/// Imports `inst` and populates instMap[inst] with the imported Value.
@ -471,7 +473,7 @@ static const DenseMap<unsigned, StringRef> opcMap = {
// FIXME: switch
// FIXME: indirectbr
// FIXME: invoke
// FIXME: resume
INST(Resume, Resume),
// FIXME: unreachable
// FIXME: cleanupret
// FIXME: catchret
@ -604,6 +606,7 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) {
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:
@ -726,8 +729,11 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) {
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);
Type ty = processType(lpi->getType());
if (!ty)
return failure();
v = b.create<LandingpadOp>(loc, ty, lpi->isCleanup(), ops);
return success();
}
case llvm::Instruction::Invoke: {
@ -798,6 +804,28 @@ LogicalResult Importer::processInstruction(llvm::Instruction *inst) {
}
}
FlatSymbolRefAttr Importer::getPersonalityAsAttr(llvm::Function *f) {
if (!f->hasPersonalityFn())
return nullptr;
llvm::Constant *pf = f->getPersonalityFn();
// If it directly has a name, we can use it.
if (pf->hasName())
return b.getSymbolRefAttr(pf->getName());
// If it doesn't have a name, currently, only function pointers that are
// bitcast to i8* are parsed.
if (auto ce = dyn_cast<llvm::ConstantExpr>(pf)) {
if (ce->getOpcode() == llvm::Instruction::BitCast &&
ce->getType() == llvm::Type::getInt8PtrTy(dialect->getLLVMContext())) {
if (auto func = dyn_cast<llvm::Function>(ce->getOperand(0)))
return b.getSymbolRefAttr(func->getName());
}
}
return FlatSymbolRefAttr();
}
LogicalResult Importer::processFunction(llvm::Function *f) {
blocks.clear();
instMap.clear();
@ -810,6 +838,13 @@ LogicalResult Importer::processFunction(llvm::Function *f) {
b.setInsertionPoint(module.getBody(), getFuncInsertPt());
LLVMFuncOp fop = b.create<LLVMFuncOp>(UnknownLoc::get(context), f->getName(),
functionType);
if (FlatSymbolRefAttr personality = getPersonalityAsAttr(f))
fop.setAttr(b.getIdentifier("personality"), personality);
else if (f->hasPersonalityFn())
emitWarning(UnknownLoc::get(context),
"could not deduce personality, skipping it");
if (f->isDeclaration())
return success();

View File

@ -99,7 +99,8 @@ llvm::Constant *ModuleTranslation::getLLVMConstant(llvm::Type *llvmType,
if (auto floatAttr = attr.dyn_cast<FloatAttr>())
return llvm::ConstantFP::get(llvmType, floatAttr.getValue());
if (auto funcAttr = attr.dyn_cast<FlatSymbolRefAttr>())
return functionMapping.lookup(funcAttr.getValue());
return llvm::ConstantExpr::getBitCast(
functionMapping.lookup(funcAttr.getValue()), llvmType);
if (auto splatAttr = attr.dyn_cast<SplatElementsAttr>()) {
auto *sequentialType = cast<llvm::SequentialType>(llvmType);
auto elementType = sequentialType->getElementType();
@ -353,6 +354,7 @@ LogicalResult ModuleTranslation::convertOperation(Operation &opInst,
if (auto constOperand = dyn_cast<llvm::Constant>(operand))
lpi->addClause(constOperand);
}
valueMapping[lpOp.getResult()] = lpi;
return success();
}
@ -585,6 +587,14 @@ LogicalResult ModuleTranslation::convertOneFunction(LLVMFuncOp func) {
argIdx++;
}
// Check the personality and set it.
if (func.personality().hasValue()) {
llvm::Type *ty = llvm::Type::getInt8PtrTy(llvmFunc->getContext());
if (llvm::Constant *pfunc =
getLLVMConstant(ty, func.personalityAttr(), func.getLoc()))
llvmFunc->setPersonalityFn(pfunc);
}
// First, create all blocks so we can jump to them.
llvm::LLVMContext &llvmContext = llvmFunc->getContext();
for (auto &bb : func) {
@ -646,8 +656,10 @@ SmallVector<llvm::Value *, 8>
ModuleTranslation::lookupValues(ValueRange values) {
SmallVector<llvm::Value *, 8> remapped;
remapped.reserve(values.size());
for (Value v : values)
for (Value v : values) {
assert(valueMapping.count(v) && "referencing undefined value");
remapped.push_back(valueMapping.lookup(v));
}
return remapped;
}

View File

@ -515,7 +515,7 @@ func @cmpxchg_failure_acq_rel(%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) {
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 @bad_landingpad(%arg0: !llvm<"i8**">) {
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 @caller(%arg0: !llvm.i32) -> !llvm.i32 {
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
@ -564,6 +564,37 @@ llvm.func @caller(%arg0: !llvm.i32) -> !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 {{'llvm.resume' op expects landingpad value as operand}}
llvm.resume %0 : !llvm.i32
}
// -----
llvm.func @foo(!llvm.i32) -> !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 }">
}
// -----
func @invalid_ordering_in_fence() {
// expected-error @+1 {{can be given only acquire, release, acq_rel, and seq_cst orderings}}
llvm.fence syncscope("agent") monotonic

View File

@ -238,7 +238,7 @@ llvm.func @bar(!llvm<"i8*">, !llvm<"i8*">, !llvm<"i8*">)
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 @@ llvm.func @invokeLandingpad() -> !llvm.i32 {
%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]] : !llvm<"{ i8*, i32 }">
^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

View File

@ -282,8 +282,7 @@ define i32 @invokeLandingpad() personality i8* bitcast (i32 (...)* @__gxx_person
; 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
resume { i8*, i32 } %3
; CHECK: ^bb2:
; CHECK: llvm.return %{{[0-9]+}} : !llvm.i32

View File

@ -1137,7 +1137,7 @@ llvm.func @bar(!llvm<"i8*">) -> !llvm<"i8*">
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]">