Closed Quafadas closed 1 year ago
Ah so for the latter, yes you should be able to just return a float since it is primitive type and an LLVM type -- try the following:
using StaticCompiler, StaticTools
@inline function add(a::Float64 , b::Float64)
return a+b
end
compile_shlib(add, (Float64, Float64), filename="libadd")
# Have to add julia_ in front because we didn't demangle and it's added by default
@ccall "libadd".julia_add(1.0::Float64, 2.0::Float64)::Float64
I get
julia> compile_shlib(add, (Float64, Float64), filename="libadd")
"/Users/cbkeller/libadd.dylib"
julia> @ccall "libadd".julia_add(1.0::Float64, 2.0::Float64)::Float64
3.0
(actually, no need for even using StaticTools
there unless you want to print or something)
For the former, it looks like you maybe have some redundancies between pointers and refs.. try instead for example
using StaticCompiler
@inline function add!(c::Ptr{Float64}, a::Ptr{Float64} , b::Ptr{Float64} )
Base.unsafe_store!(c, Base.unsafe_load(a) + Base.unsafe_load(b))
return 0
end
compile_shlib(add!, (Ptr{Float64}, Ptr{Float64},Ptr{Float64}), filename="libadd")
a,b,c = Ref.((1., 2., 0.))
pa,pb,pc = Ptr{Float64}.(pointer_from_objref.((a,b,c)))
# Have to add julia_ in front because we didn't demangle
# Note that the ! becomes a _
@ccall "libadd".julia_add_(pc::Ptr{Float64}, pa::Ptr{Float64}, pb::Ptr{Float64})::Int64
I get
julia> @ccall "libadd".julia_add_(pc::Ptr{Float64}, pa::Ptr{Float64}, pb::Ptr{Float64})::Int64
0
julia> c
Base.RefValue{Float64}(3.0)
In the latter example the Ref
s are basically just a convenient way to get pointers to somewhere where the Float64
s are allocated on the heap -- which in this case isn't especially necessary, but would be if they were something other than a primitive type that directly corresponds to an LLVM type
In all cases, the tuple of types is just to tell StaticCompiler what to compile.
Sometimes if you have overwritten the method you want to compile several times (by re-defining it) it seems like staticcompiler/gpucompiler may get confused, so it may sometimes help to start a new session if it seems like you're trying to compile a method that really should return a concrete type (though 99 times out of 100 it's something more like a typo)
Yes! Thankyou so much. I think that first example, is a lot of what I needed to know.
println("Julia add 1 + 2")
val summed = addLib.julia_add(1, 2)
println(s"sums too = $summed")
This runs (apparently successfully) ... but produces a puzzling result when called from scala.
julia add 1 + 2
sums too = Infinity
I would guess, that something about CFloat, is not quite consistent with Julia's Int64. But I think this shows it quite concretely - it may be a tractable problem. Never have I been so excited about bad mathematics.
Thankyou!
Int64 would be CLongLong, not CFloat. Can you try that?
Edit: the C types are only valid for bindings to C. Julia's In64 is directly equivalent to Long in Scala.
So ... trying to use a float for a Julia int - not my smartest moment! Using both Long and CLongLong, I get
julia add 1 + 2
0
Which means I'm infinitely closer :-). I'll look again tomorrow, in case I have done something stupid. It's feels like this really might be tractable though!
Sounds like progress. You might also make sure that's the version that returns the result c
rather than returning 0
.
I'll close the issue, but feel free to keep discussing here!
Well, I have some progress in that primitives now seem to work. I do not yet however, appear to have reached the stage, where I can innovate on my knowledge :-( ...
using StaticCompiler
using StaticTools
@inline function hi(c::Ptr{MallocString}, a::Ptr{MallocString}) :: Int64
load_a = Base.unsafe_load(a)
StaticTools.printf("entered julia")
StaticTools.printf(load_a)
tmp = c"hi right back from julia"
Base.unsafe_store!(c, tmp)
return 0
end
compile_shlib(hi, (Ptr{MallocString}, Ptr{MallocString}), filename="string_pointers")
s1 :: MallocString = c"hi1"
s2 :: MallocString = c"hi2"
s1_p :: Ptr{MallocString}, s2_p :: Ptr{MallocString} = pointer_from_objref(Ref(s1)), pointer_from_objref(Ref(s2))
@ccall "/workspaces/slincTest/myJlib/string_pointers.so".julia_hi(s1_p:: Ptr{MallocString} ,s2_p:: Ptr{MallocString}) :: Int64
s1_p
Gets me a segmenetation fault. Am I doing something obviously wrong here?
Ah, a few things:
1) Your function is expecting pointers to MallocStrings but you're giving it pointers to StaticStrings
2) the line StaticTools.printf("entered julia")
has a regular string, which will cause problems
3) the line s1_p :: Ptr{MallocString}, s2_p :: Ptr{MallocString} = pointer_from_objref(Ref(s1)), pointer_from_objref(Ref(s2))
makes Julia's garbage collector think that you never need the Ref
s because it looks like you throw them away right away (it doesn't know you're keeping a pointer around), so either GC.@preserve
or just put on a separate line like we did before. This will segfault if the Refs are GC'd
4) the line Base.unsafe_store!(c, tmp)
is going to try to write a StaticString (tmp
) to a pointer that's supposed to hold a MallocString. A MallocString is just a pointer and a length, while a StaticString includes a tuple of data, so that won't work as is. It could work if you made tmp
a MallocString instead of a StaticString, or you could just write to the string c
rather than replacing it.
Try instead:
using StaticCompiler, StaticTools
@inline function hi(c::Ptr{MallocString}, a::Ptr{MallocString})::Int64
load_a = Base.unsafe_load(a)
StaticTools.printf(c"entered julia\n")
StaticTools.printf(load_a)
load_c = Base.unsafe_load(c)
tmp = c"hi right back from julia"
load_c[:] = tmp # This basically does copyto, could also loop through the individual characters / UInt8s.
return 0
end
compile_shlib(hi, (Ptr{MallocString}, Ptr{MallocString}), filename="string_pointers")
sa = m"hi1"
sc = m"This string has to be big enough to hold your return message"
ra, rc = Ref.((sa, sc))
pa, pc = Ptr{MallocString}.(pointer_from_objref.((ra, rc))) # The pointer typecast is because pointer_from_objref returns Ptr{nothing}
@ccall "string_pointers".julia_hi(pc::Ptr{MallocString},pa::Ptr{MallocString})::Int64
I get
julia> @ccall "string_pointers".julia_hi(pc::Ptr{MallocString},pa::Ptr{MallocString})::Int64
0
julia> sc
m"hi right back from julia"
@brenhinkeller That is a next level answer. Thankyou so much again. I guess you can just about tell this is more or less a first foray into unmanaged memory for me! Thankyou again so much for your patience and answers. I can follow your example which I can successfully call this from Julia itself, which is great.
I haven't managed to make the final call from scala though... I feel like I'm now either very close ... or very far!
warning: Using incubator modules: jdk.incubator.foreignException in thread "main" java.lang.UnsatisfiedLinkError: /workspaces/slincTest/myJlib/string_pointers.so: /workspaces/slincTest/myJlib/string_pointers.so: undefined symbol:
__stack_chk_guard at java.base/jdk.internal.loader.NativeLibraries.load(Native Method) at
java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:388) at
java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:232) at
java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:174) at
java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2389) at
java.base/java.lang.Runtime.load0(Runtime.java:755) at java.base/java.lang.System.load(System.java:1953) at
fr.hammons.slinc.modules.LinkageTools$.load(LinkageTools.scala:65) at
fr.hammons.slinc.modules.LinkageTools$.loadDependency(LinkageTools.scala:100) at
fr.hammons.slinc.modules.FSetModule17$package$fsetModule17$.getBacking$$anonfun$1(FSetModule17.scala:23) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at
scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.immutable.List.foreach(List.scala:333) at fr.hammons.slinc.modules.FSetModule17$package$fsetModule17$.getBacking(FSetModule17.scala:23) at fr.hammons.slinc.FSet.get(FSet.scala:26) at fr.hammons.slinc.FSet.get$(FSet.scala:13) at slinj.stringTest$$anon$6.get(slinc.scala:58) at slinj.slinc$package$.<clinit>(slinc.scala:64) at slinj.calc.main(slinc.scala:84)
The nature of the message and success from Julia makes me think that the Julia library I've just compiled, relies on some utility, that I have not managed to correctly include during compilation, but that happens to be present in the scope of StaticTools / Compiler, when called from Julia itself.
Could that be the case?
@markehammons cc/ in case this could be ( I don't think it is) to do with slinc?
Ah interesting -- I don't know what this message means because I don't know scala or java, but if someone else does hopefully they can chime in.
Some of this stuff is new to everyone, since Julia is certainly not designed with the intention of people manually managing their memory!
There's an unsatisfied link error causing a missing symbol. This basically means there's missing definitions in the library you loaded. Specifically __stack_chk_guard.
It looks like this might be a rehash of an old problem?
This is a "discussion" or request for help rather than an issue. Apologies if it's the wrong place, I couldn't see "discussions".
I've successfully run the matrix multiplication example in the readme.
I was trying to simplify it even further, to understand more closely the nature of the constraints in place with "static compiler".
Errors out with
I had also believed, that something like this, ought to be possible, as floats are primitives?
But I get the same message. I suspect, that my understanding of the role of the type tuple, is incomplete somehow.
Would anyone have a hint?
Full diclosure; my aim is to call a statically compiled Julia lib from scala, and gradually increase the complexity https://quafadas-literate-space-goldfish-g5qw954xj6f9qgp.github.dev/