Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions juliac/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Manifest.toml
recfact_server
recfact_debug
*.so
*.o
build/
test_*
3 changes: 3 additions & 0 deletions juliac/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[deps]
RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
72 changes: 72 additions & 0 deletions juliac/shim.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module RecursiveFactorizationShim

import RecursiveFactorization
using LinearAlgebra: BlasInt
import Base.@ccallable

# ============================================================================
# @ccallable entry points for RecursiveFactorization.lu!
#
# Interface mirrors LAPACK dgetrf/sgetrf:
# A: pointer to column-major matrix data (modified in-place to L\U)
# m: number of rows
# n: number of columns
# ipiv: pointer to pivot index array (length min(m,n)), output
# returns: info (0 = success, k > 0 = U(k,k) is exactly zero)
# ============================================================================

# --- Float64, with pivoting, no threading ---
@ccallable function recursive_lu_f64!(A::Ptr{Float64}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float64}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(true), Val(false); check = false)
return Int64(F.info)
end

# --- Float32, with pivoting, no threading ---
@ccallable function recursive_lu_f32!(A::Ptr{Float32}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float32}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(true), Val(false); check = false)
return Int64(F.info)
end

# --- Float64, with pivoting, threaded ---
@ccallable function recursive_lu_f64_threaded!(A::Ptr{Float64}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float64}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(true), Val(true); check = false)
return Int64(F.info)
end

# --- Float32, with pivoting, threaded ---
@ccallable function recursive_lu_f32_threaded!(A::Ptr{Float32}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float32}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(true), Val(true); check = false)
return Int64(F.info)
end

# --- Float64, no pivoting, no threading ---
@ccallable function recursive_lu_f64_nopiv!(A::Ptr{Float64}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float64}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(false), Val(false); check = false)
return Int64(F.info)
end

# --- Float32, no pivoting, no threading ---
@ccallable function recursive_lu_f32_nopiv!(A::Ptr{Float32}, m::Int64, n::Int64,
ipiv::Ptr{Int64})::Int64
mat = unsafe_wrap(Matrix{Float32}, A, (m, n))
ipiv_vec = unsafe_wrap(Vector{Int64}, ipiv, min(m, n))
F = RecursiveFactorization.lu!(mat, ipiv_vec, Val(false), Val(false); check = false)
return Int64(F.info)
end

end # module
125 changes: 125 additions & 0 deletions juliac/shim_exe.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Signal to RecursiveFactorization.__init__ that we ARE the server binary,
# so it should not try to start a subprocess server (which would recurse).
ENV["RECFACT_SERVER"] = "1"

import RecursiveFactorization
using LinearAlgebra: BlasInt

# Low-level I/O using libc read/write on file descriptors
# This avoids needing Core.stdin which isn't available in trimmed binaries

# Set a file descriptor to blocking mode.
# The juliac runtime's libuv may set fds to non-blocking, which breaks raw read/write.
function set_blocking!(fd::Cint)::Nothing
flags = ccall(:fcntl, Cint, (Cint, Cint), fd, 3) # F_GETFL = 3
flags == -1 && error("fcntl F_GETFL failed")
# Clear O_NONBLOCK (0x800 on Linux)
new_flags = flags & ~Cint(0x800)
ret = ccall(:fcntl, Cint, (Cint, Cint, Cint), fd, 4, new_flags) # F_SETFL = 4
ret == -1 && error("fcntl F_SETFL failed")
nothing
end

function fd_read!(fd::Cint, buf::Ptr{UInt8}, nbytes::Int)::Nothing
remaining = nbytes
offset = 0
while remaining > 0
n = ccall(:read, Cssize_t, (Cint, Ptr{UInt8}, Csize_t), fd, buf + offset, remaining)
n <= 0 && error("read failed")
remaining -= n
offset += n
end
nothing
end

function fd_write(fd::Cint, buf::Ptr{UInt8}, nbytes::Int)::Nothing
remaining = nbytes
offset = 0
while remaining > 0
n = ccall(:write, Cssize_t, (Cint, Ptr{UInt8}, Csize_t), fd, buf + offset, remaining)
n <= 0 && error("write failed")
remaining -= n
offset += n
end
nothing
end

function read_value(fd::Cint, ::Type{T}) where {T}
buf = Ref{T}()
GC.@preserve buf fd_read!(fd, Ptr{UInt8}(pointer_from_objref(buf)), sizeof(T))
return buf[]
end

function write_value(fd::Cint, x::T) where {T}
buf = Ref{T}(x)
GC.@preserve buf fd_write(fd, Ptr{UInt8}(pointer_from_objref(buf)), sizeof(T))
nothing
end

function read_matrix!(fd::Cint, A::AbstractArray)
GC.@preserve A fd_read!(fd, Ptr{UInt8}(pointer(A)), sizeof(eltype(A)) * length(A))
nothing
end

function write_array(fd::Cint, A::AbstractArray)
GC.@preserve A fd_write(fd, Ptr{UInt8}(pointer(A)), sizeof(eltype(A)) * length(A))
nothing
end

function process_f64(fdin::Cint, fdout::Cint, m::Int64, n::Int64, mn::Int64,
pivot::Val, thread::Val)
A = Matrix{Float64}(undef, m, n)
read_matrix!(fdin, A)
ipiv = Vector{Int64}(undef, mn)
F = RecursiveFactorization.lu!(A, ipiv, pivot, thread; check = false)
write_value(fdout, Int64(F.info))
write_array(fdout, A)
write_array(fdout, ipiv)
return nothing
end

function process_f32(fdin::Cint, fdout::Cint, m::Int64, n::Int64, mn::Int64,
pivot::Val, thread::Val)
A = Matrix{Float32}(undef, m, n)
read_matrix!(fdin, A)
ipiv = Vector{Int64}(undef, mn)
F = RecursiveFactorization.lu!(A, ipiv, pivot, thread; check = false)
write_value(fdout, Int64(F.info))
write_array(fdout, A)
write_array(fdout, ipiv)
return nothing
end

function (@main)(args::Vector{String})
fdin = Cint(0) # stdin fd
fdout = Cint(1) # stdout fd

# The juliac runtime (libuv) may set fds to non-blocking mode.
# Reset to blocking for our raw read/write calls.
set_blocking!(fdin)
set_blocking!(fdout)

while true
cmd = read_value(fdin, UInt8)
cmd == 0xff && break

m = read_value(fdin, Int64)
n = read_value(fdin, Int64)
mn = min(m, n)

if cmd == 0x00
process_f64(fdin, fdout, m, n, mn, Val(true), Val(false))
elseif cmd == 0x01
process_f32(fdin, fdout, m, n, mn, Val(true), Val(false))
elseif cmd == 0x02
process_f64(fdin, fdout, m, n, mn, Val(true), Val(true))
elseif cmd == 0x03
process_f32(fdin, fdout, m, n, mn, Val(true), Val(true))
elseif cmd == 0x04
process_f64(fdin, fdout, m, n, mn, Val(false), Val(false))
elseif cmd == 0x05
process_f32(fdin, fdout, m, n, mn, Val(false), Val(false))
end
end
return 0
end
6 changes: 6 additions & 0 deletions src/RecursiveFactorization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ if isdefined(Base, :Experimental) &&
end
include("./lu.jl")
include("./butterflylu.jl")
include("./juliac_server.jl")

import PrecompileTools

PrecompileTools.@compile_workload begin
lu!(rand(2, 2))
end

function __init__()
_init_juliac_server()
atexit(_finalize_juliac_server)
end

end # module
Loading
Loading