6
$\begingroup$

On the Wolfram official website, there is an example of calling library function in a compiled function:

Needs["CompiledFunctionTools`"]

fun=LibraryFunctionLoad[FileNameJoin[{NotebookDirectory[],"demo.dylib"}],"demo_I_I",{Integer},Integer];

c=Compile[{{x,_Integer}},fun[x],CompilationOptions->{"InlineExternalDefinitions" -> True,"InlineCompiledFunctions"->True},CompilationTarget->"C"];

CompilePrint@c

(*
    1 argument
    2 Integer registers
    Underflow checking off
    Overflow checking off
    Integer overflow checking on
    RuntimeAttributes -> {}

    I0 = A1
    Result = I1

1   I1 = LibraryFunction[<>, demo_I_I, {Integer}, Integer][ I0]]
2   Return

*)

However, if I try it with my function (code at the end) with shared passing, the library function is not linked correctly, and a MainEvaluate is presented in the compiled code:

add1lib = CreateLibrary[add1src, "add1"];
add1 = 
 LibraryFunctionLoad[add1lib, "add1", {{Real, 1, "Shared"}}, "Void"];

f = Compile[{{n, _Integer}}, Module[{ls},
   ls = Table[i/1., {i, 1, n}];
   a[ls];
   ls
   ]]

CompilePrint@f

(*
.....       
10  V17T(R1)0 = MainEvaluate[ Function[{n, lsCompile$1}, Block[{ls = lsCompile$1}, {a[ls], ls}]][ I0, T(R1)0]]
11  Return
*)

Here is a test of this function:

a = {1., 2.} // Developer`ToPackedArray;
add1[a]
a
(* {2., 3.} *)

Note that in my function, I use shared passing of argument, so that I can modify them inside the library function.

Question:

Why does it break in my case? How to make it work?


add1src

#include "WolframLibrary.h"

/* Return the version of Library Link */
DLLEXPORT mint WolframLibrary_getVersion( ) {
    return WolframLibraryVersion;
}

/* Initialize Library */
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData) {
    return LIBRARY_NO_ERROR;
}

/* Uninitialize Library */
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData) {
    return;
}


DLLEXPORT int add1( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {

    MTensor tx = MArgument_getMTensor(Args[0]);
    double* x = libData->MTensor_getRealData(tx);

    mint N=*(libData->MTensor_getDimensions(tx));
    mint i;

    for(i=0;i<N;i++) x[i]=x[i]+1.;

    return LIBRARY_NO_ERROR;
}
$\endgroup$
3
$\begingroup$

It doesn't work because your function returns Void and for some reason Compile can't handle that.

If you cheat Mathematica and tell it that your function returns an Integer:

add1NonVoid =
    LibraryFunctionLoad[add1lib, "add1", {{Real, 1, "Shared"}}, Integer];

a = {1., 2.} // Developer`ToPackedArray;
add1NonVoid[a]
(* 0 *)
a
(* {2., 3.} *)

then it will be called directly, without MainEvaluate:

f = Compile[{{n, _Integer}},
    Module[{ls},
        ls = Table[i/1., {i, 1, n}];
        add1NonVoid[ls];
        ls
    ],
    CompilationOptions -> {"InlineExternalDefinitions" -> True}
];
CompilePrint@f
(*
...
10  I3 = LibraryFunction[<>, add1, {{Real, 1, Shared}}, Integer][ T(R1)0]]
11  Return
*)

f[5]
(* {2., 3., 4., 5., 6.} *)

I don't really know what happens under the hood when Mathematica returns integer from function that really returns void, so I can't recommend doing something like this.


Handling of void in Compile is buggy not only in case of calling LibraryFunction, but also other CompiledFunctions.

Compiling following to virtual machine works fine:

returnNothing = Compile[{{lst, _Real, 1}}, lst + 1;];
Compile[{{n, _Integer}},
    returnNothing[{1., 2.}];
    ,
    CompilationOptions -> {
        "InlineExternalDefinitions" -> True,
        "InlineCompiledFunctions" -> False
    },
    CompilationTarget -> "WVM"
];
CompilePrint@%
(*
...
1   V17 = CompiledFunctionCall[ Hold[CompiledFunction[{lst}, lst + 1; , -CompiledCode-]][ T(R1)0]]
2   Return
*)

but you can't compile it to "C" (of course you could use "InlineCompiledFunctions" -> True here and it would work, but that's not the point).

Compile[{{n, _Integer}},
    returnNothing[{1., 2.}];
    ,
    CompilationOptions -> {
        "InlineExternalDefinitions" -> True, 
        "InlineCompiledFunctions" -> False
    },
    CompilationTarget -> "C"
];
(*
StringJoin::string: String expected at position 1 in CCodeGenerator`Private`typeToVariable[Void]<>0_17. >>
...
Compile::nogen: A library could not be generated from the compiled function. >>
*)
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Thanks for the answer. It surprises me that Compile can't handle void return. I would guess void return would be quite common. I'm wondering how they deal that inside wolfram. Would you consider that as a bug? $\endgroup$ – xslittlegrass Aug 28 '15 at 13:55
  • $\begingroup$ @xslittlegrass I was as surprised as you are. I'm not sure if this is a bug since it's all scarcely documented. And in documentation of Compile void is not mentioned in "The types handled by Compile", but I'd definitely like to see it fixed/changed/enhanced. $\endgroup$ – jkuczm Aug 28 '15 at 14:04
  • $\begingroup$ I think this is important, so I will tag it with bugs so maybe people will be aware of that. It took me a while to isolate this problem from my code. $\endgroup$ – xslittlegrass Aug 28 '15 at 14:07
  • $\begingroup$ I agree that it's a bug. When you get an unexpected StringJoin::string message by doing something that to all appearances should be perfectly allowable, I don't think we can describe it any other way. $\endgroup$ – Oleksandr R. Aug 28 '15 at 16:31
  • 1
    $\begingroup$ @OleksandrR. Ok, I've reported it: [CASE:3410009] $\endgroup$ – jkuczm Aug 28 '15 at 17:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.