diff --git a/.github/workflows/ci-integration.yml b/.github/workflows/ci-integration.yml index beb16c1..544b0a4 100644 --- a/.github/workflows/ci-integration.yml +++ b/.github/workflows/ci-integration.yml @@ -24,12 +24,17 @@ jobs: os: - ubuntu-latest - macOS-latest + - windows-latest arch: - x64 group: - Integration steps: - uses: actions/checkout@v2 + - uses: KyleMayes/install-llvm-action@v2 + with: + version: "17" + if: matrix.os == 'windows-latest' - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dc141f..499223c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: os: - ubuntu-latest - macOS-latest + - windows-latest arch: - x64 group: @@ -34,6 +35,10 @@ jobs: os: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: KyleMayes/install-llvm-action@v2 + with: + version: "17" + if: matrix.os == 'windows-latest' - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} diff --git a/src/StaticCompiler.jl b/src/StaticCompiler.jl index 38547fe..90e3404 100644 --- a/src/StaticCompiler.jl +++ b/src/StaticCompiler.jl @@ -23,6 +23,7 @@ include("interpreter.jl") include("target.jl") include("pointer_warning.jl") include("quirks.jl") +include("dllexport.jl") fix_name(f::Function) = fix_name(string(nameof(f))) fix_name(s) = String(GPUCompiler.safe_name(s)) @@ -125,6 +126,7 @@ function compile_executable(funcs::Union{Array,Tuple}, path::String=pwd(), name= nativetype || @warn "Return type `$rt` of `$f$types` does not appear to be a native type. Consider returning only a single value of a native machine type (i.e., a single float, int/uint, bool, or pointer). \n\nIgnoring this warning may result in Undefined Behavior!" generate_executable(funcs, path, name, filename; demangle, cflags, target, llvm_to_clang, kwargs...) + Sys.iswindows() && (filename *= ".exe") joinpath(abspath(path), filename) end @@ -187,6 +189,7 @@ function compile_shlib(funcs::Union{Array,Tuple}, path::String=pwd(); demangle = true, cflags = ``, target::StaticTarget=StaticTarget(), + llvm_to_clang = Sys.iswindows(), kwargs... ) for func in funcs @@ -200,7 +203,7 @@ function compile_shlib(funcs::Union{Array,Tuple}, path::String=pwd(); nativetype || @warn "Return type `$rt` of `$f$types` does not appear to be a native type. Consider returning only a single value of a native machine type (i.e., a single float, int/uint, bool, or pointer). \n\nIgnoring this warning may result in Undefined Behavior!" end - generate_shlib(funcs, path, filename; demangle, cflags, target, kwargs...) + generate_shlib(funcs, path, filename; demangle, cflags, target, llvm_to_clang, kwargs...) joinpath(abspath(path), filename * "." * Libdl.dlext) end @@ -325,17 +328,18 @@ function generate_executable(funcs::Union{Array,Tuple}, path=tempname(), name=fi if llvm_to_clang # (required on Windows) # Use clang (llc) to generate an executable from the LLVM IR cclang = if Sys.iswindows() - `cmd \c clang` # Not clear if the `cmd \c` is necessary + exec_path *= ".exe" + `clang` elseif Sys.isapple() `clang` else clang() end - run(`$cclang -Wno-override-module $wrapper_path $obj_or_ir_path -o $exec_path`) + run(`$cclang -Wno-override-module $wrapper_path $obj_or_ir_path -o $exec_path`) else run(`$cc $wrapper_path $cflags $obj_or_ir_path -o $exec_path`) end - + # Clean up rm(wrapper_path) end @@ -391,6 +395,7 @@ function generate_shlib(funcs::Union{Array,Tuple}, path::String=tempname(), file demangle = true, cflags = ``, target::StaticTarget=StaticTarget(), + llvm_to_clang::Bool = Sys.iswindows(), kwargs... ) if !isnothing(target.platform) @@ -399,7 +404,7 @@ function generate_shlib(funcs::Union{Array,Tuple}, path::String=tempname(), file lib_path = joinpath(path, "$filename.$(Libdl.dlext)") end - _, obj_path = generate_obj(funcs, path, filename; target, demangle, kwargs...) + _, obj_or_ir_path = generate_obj(funcs, path, filename; demangle, target, emit_llvm_only=llvm_to_clang, kwargs...) # Pick a Clang if !isnothing(target.compiler) cc = `$(target.compiler)` @@ -407,7 +412,20 @@ function generate_shlib(funcs::Union{Array,Tuple}, path::String=tempname(), file cc = Sys.isapple() ? `cc` : clang() end # Compile! - run(`$cc -shared $cflags $obj_path -o $lib_path `) + if llvm_to_clang # (required on Windows) + # Use clang (llc) to generate an executable from the LLVM IR + cclang = if Sys.iswindows() + add_dllexport(funcs, obj_or_ir_path; demangle) + `clang` + elseif Sys.isapple() + `clang` + else + clang() + end + run(`$cclang -shared -Wno-override-module $obj_or_ir_path -o $lib_path`) + else + run(`$cc -shared $cflags $obj_or_ir_path -o $lib_path `) + end path, name end diff --git a/src/dllexport.jl b/src/dllexport.jl new file mode 100644 index 0000000..b957e7b --- /dev/null +++ b/src/dllexport.jl @@ -0,0 +1,11 @@ +function add_dllexport(funcs, ir_path; demangle=true) + ir = read(ir_path, String) + + for (f, _) in funcs + name_f = (demangle ? "" : "julia_") * fix_name(f) + pattern = Regex("^define(.*?@$name_f\\()", "m") + ir = replace(ir, pattern => s"define dllexport\1") + end + + write(ir_path, ir) +end \ No newline at end of file diff --git a/test/testintegration.jl b/test/testintegration.jl index 3422e63..d2a98da 100644 --- a/test/testintegration.jl +++ b/test/testintegration.jl @@ -9,7 +9,7 @@ if VERSION >= v"1.9" buf = AllocBuffer(MallocVector, sizeof(Float64) * N) s = 0.0 for i ∈ 1:N - # some excuse to reuse the same memory a bunch of times + # some excuse to reuse the same memory a bunch of times @no_escape buf begin v = @alloc(Float64, N) v .= i @@ -24,7 +24,7 @@ if VERSION >= v"1.9" path = compile_shlib(bumper_test, (Int,), "./") ptr = Libdl.dlopen(path, Libdl.RTLD_LOCAL) - + fptr = Libdl.dlsym(ptr, "bumper_test") @test bumper_test(8) == @ccall($fptr(8::Int)::Float64) @@ -303,7 +303,12 @@ end catch e @info "maybe_throw: task failed sucessfully!" end - @test status === -1 + if Sys.iswindows() + @info "maybe_throw: task doesn't fail on Windows." + @test status.exitcode == 0 + else + @test status === -1 + end end ## --- Test interop