Mlir-translate ignores mlir file

Hi all,

My experience is limited to LLVM CAPI and I’m new to MLIR. In my experience (very short), if mlir-opt passes the input mlir then mlir-translate will generate the final IR.

In my exemplar I’m calling regcomp from MLIR CAPI (12.0.1). See my reproducer below (mlir-opt -convert-std-to-llvm):

module attributes {llvm.data_layout = ""}  {
  llvm.func @__match(%arg0: !llvm.ptr<i8>, %arg1: !llvm.ptr<i8>) -> i32 attributes {arg_attrs = [{llvm.name = "str"}, {llvm.name = "pattern"}]} {
    %0 = llvm.mlir.null : !llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>
    %1 = llvm.mlir.constant(0 : i32) : i32
    llvm.call @regcomp(%0, %arg1, %1) : (!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> ()
    %2 = llvm.mlir.constant(1 : i32) : i32
    llvm.return %2 : i32
  }
  llvm.func @regcomp(!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> i32
}

However if I run this reproducer through mlir-translate --mlir-to-llvmir I don’t get any LLVM IR. I suspect the issue is the llvm.call and it’s arguments but mlir-opt doesn’t complain.

regards,
Arun

When I try to run your example it fails, but not silently:

$ ./bin/mlir-translate --mlir-to-llvmir /tmp/t.mlir 
/tmp/t.mlir:9:5: error: LLVM Translation failed for operation: llvm.call
    llvm.call @regcomp(%0, %arg1, %1) : (!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> ()
    ^
/tmp/t.mlir:9:5: note: see current operation: "llvm.call"(%0, %arg1, %1) {callee = @regcomp} : (!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> ()

So the llvm.call conversion failed, it seems that it converts it into:

  %3 = call i32 @regcomp(%regex_t* null, i8* %1, i32 0), !dbg !7

but consider this a failure because now it produces an SSA value where the original call didn’t have one.
This is because your MLIR call does not match the intrinsic signature, if you change it to:

%r = llvm.call @regcomp(%0, %arg1, %1) : (!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> (i32)

then it just works.

@ftynse is OOO for a couple more days, but I’m curious about the lack of diagnostics here: we should clarify the expectation of the LLVMTranslationDialectInterface::convertOperation interface: it can return an error but it isn’t clear to me if we intend for this to be recoverable? Otherwise the failing path could emit a better diagnostic here.

1 Like

thanks :slightly_smiling_face: clearly, I’ve missed some basics. Working reproducer below:

module attributes {llvm.data_layout = ""}  {
  llvm.func @__match(%arg0: !llvm.ptr<i8>, %arg1: !llvm.ptr<i8>) -> i32 attributes {arg_attrs = [{llvm.name = "str"}, {llvm.name = "pattern"}]} {
    %0 = llvm.mlir.constant(0 : i64) : i64
    %1 = llvm.alloca %0 x !llvm.struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)> : (i64) -> !llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>
    %2 = llvm.mlir.constant(0 : i32) : i32
    %3 = llvm.call @regcomp(%1, %arg1, %2) : (!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> i32
    %4 = llvm.mlir.constant(1 : i32) : i32
    llvm.return %4 : i32
  }
  llvm.func @regcomp(!llvm.ptr<struct<"regex_t", (i32, i32, ptr<i8>, ptr<struct<"re_guts", opaque>>)>>, !llvm.ptr<i8>, i32) -> i32
}

Despite the diagnostic opportunity, it’s amazing how well the type checker is providing feedback on non trivial types.

regards,
Arun

I do see a diagnostic, the same as you do, coming from here llvm-project/ModuleTranslation.cpp at 1cc29f027fe433770a478bb0a67956c5646f61b2 · llvm/llvm-project · GitHub

We did not intend for translation errors to be recoverable. We could make the translation infrastructure more similar to the pattern rewriting infrastructure, in particular, make it try different translations for the same operation, but there was no reasonable use case so far.

More detailed error message could come from the code that translates a specific operation, which currently just does return failure() but could be doing return op.emitError() << "message". It would be better to update all of that code to produce messages though, for consistency.

IMO, the actual issue here is the verifier not catching the function signature mismatch, i.e., mlir-opt round-tripping successfully.

1 Like

⚙ D106937 [mlir] harden result type verification in llvm.call adds the relevant verifications and ⚙ D106938 [mlir] run the verifier before translating a module makes sure we actually run them.

1 Like