Skip to content

Commit 698bb5f

Browse files
[mlir][docs] Add C example for C-compatible wrapper for LLVM IR (#120955)
`TargetLLVMIR` documentation introduced the C-compatible wrapper function for a MLIR function and ways to generate it, but did not demonstrate the corresponding C function signature for them. The C function signature is not obvious, in that * `MemrefDescriptor` should be passed as _pointer_. + For example, MLIR function could return a new Descriptor, so pointer is a must. + Surprisingly, directly pass the struct, by C convention, is also a pointer so some function will work, but that is implicit and error-prone. * for `@foo() -> memref<>`, the return type becomes the first argument in `_mlir_ciface_foo(%arg0: !llvm.ptr)`. + This is described in https://github.com/llvm/llvm-project/blob/f70ab7d909d6861c7eec5ab40679bde16ab826c6/mlir/lib/Conversion/FuncToLLVM/FuncToLLVM.cpp#L110-L167 Especially by code `size_t argOffset = resultStructType ? 1 : 0;` saying the actual argument starts at 1 when result is a struct (memref) Users using the wrong signature will get incorrect results. LLVM discourse has some example of it * https://discourse.llvm.org/t/how-to-compile-and-link-with-other-c-c-programs/4835/10 * https://discourse.llvm.org/t/segmentation-fault-on-memref-store/80286/3 * https://discourse.llvm.org/t/memref-store-storing-a-memref-load/80307 Cc @ftynse for relevent commit history. Cc @charitha22 and @Wheest from discourse post.
1 parent cbe583b commit 698bb5f

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

mlir/docs/TargetLLVMIR.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ Examples:
646646
647647
```mlir
648648
649-
func.func @qux(%arg0: memref<?x?xf32>)
649+
func.func @qux(%arg0: memref<?x?xf32>) attributes {llvm.emit_c_interface}
650650
651651
// Gets converted into the following
652652
// (using type alias for brevity):
@@ -683,8 +683,18 @@ llvm.func @qux(%arg0: !llvm.ptr, %arg1: !llvm.ptr,
683683
llvm.func @_mlir_ciface_qux(!llvm.ptr)
684684
```
685685

686+
687+
```cpp
688+
// The C function implementation for the interface function.
689+
extern "C" {
690+
void _mlir_ciface_qux(MemRefDescriptor<float, 2> *input) {
691+
// detailed impl
692+
}
693+
}
694+
```
695+
686696
```mlir
687-
func.func @foo(%arg0: memref<?x?xf32>) {
697+
func.func @foo(%arg0: memref<?x?xf32>) attributes {llvm.emit_c_interface} {
688698
return
689699
}
690700
@@ -719,8 +729,15 @@ llvm.func @_mlir_ciface_foo(%arg0: !llvm.ptr) {
719729
}
720730
```
721731

732+
```cpp
733+
// The C function signature for the interface function.
734+
extern "C" {
735+
void _mlir_ciface_foo(MemRefDescriptor<float, 2> *input);
736+
}
737+
```
738+
722739
```mlir
723-
func.func @foo(%arg0: memref<?x?xf32>) -> memref<?x?xf32> {
740+
func.func @foo(%arg0: memref<?x?xf32>) -> memref<?x?xf32> attributes {llvm.emit_c_interface} {
724741
return %arg0 : memref<?x?xf32>
725742
}
726743
@@ -744,6 +761,7 @@ llvm.func @foo(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: i64,
744761
}
745762
746763
// Interface function callable from C.
764+
// NOTE: the returned memref becomes the first argument
747765
llvm.func @_mlir_ciface_foo(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
748766
%0 = llvm.load %arg1 : !llvm.ptr
749767
%1 = llvm.extractvalue %0[0] : !llvm.memref_2d
@@ -760,6 +778,14 @@ llvm.func @_mlir_ciface_foo(%arg0: !llvm.ptr, %arg1: !llvm.ptr) {
760778
}
761779
```
762780

781+
```cpp
782+
// The C function signature for the interface function.
783+
extern "C" {
784+
void _mlir_ciface_foo(MemRefDescriptor<float, 2> *output,
785+
MemRefDescriptor<float, 2> *input);
786+
}
787+
```
788+
763789
Rationale: Introducing auxiliary functions for C-compatible interfaces is
764790
preferred to modifying the calling convention since it will minimize the effect
765791
of C compatibility on intra-module calls or calls between MLIR-generated

0 commit comments

Comments
 (0)