From e672f352352097108e3049383e1edfcf508c57af Mon Sep 17 00:00:00 2001 From: Thomas R Date: Mon, 4 Mar 2024 21:27:33 +0100 Subject: [PATCH] adaption to Windows (#151) * adaption to windows: use clang to generate executable, skip GPUCompiler * a number of simplifications and bugfixes --------- Co-authored-by: C. Brenhin Keller --- .github/workflows/ci.yml | 2 +- Project.toml | 2 +- src/StaticCompiler.jl | 70 +++++++++++++++++++++++++++------------- test/testcore.jl | 10 ++++++ 4 files changed, 59 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bbb9fd..3dc141f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: version: - '1.8' - '1.9' - - '1.10.0-rc1' + - '1.10' os: - ubuntu-latest - macOS-latest diff --git a/Project.toml b/Project.toml index 96c94ea..4f41e3e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "StaticCompiler" uuid = "81625895-6c0f-48fc-b932-11a18313743c" authors = ["Tom Short and contributors"] -version = "0.7.0" +version = "0.7.1" [deps] diff --git a/src/StaticCompiler.jl b/src/StaticCompiler.jl index 7277b2f..38547fe 100644 --- a/src/StaticCompiler.jl +++ b/src/StaticCompiler.jl @@ -35,6 +35,7 @@ compile_executable(f::Function, types::Tuple, path::String, [name::String=string cflags=``, # Specify libraries you would like to link against, and other compiler options here also_expose=[], target::StaticTarget=StaticTarget(), + llvm_to_clang = Sys.iswindows(), method_table=StaticCompiler.method_table, kwargs... ) @@ -98,17 +99,18 @@ shell> ./hello Hello, world! ``` """ -function compile_executable(f::Function, types=(), path::String="./", name=fix_name(f); +function compile_executable(f::Function, types=(), path::String=pwd(), name=fix_name(f); also_expose=Tuple{Function, Tuple{DataType}}[], target::StaticTarget=StaticTarget(), kwargs...) compile_executable(vcat([(f, types)], also_expose), path, name; target, kwargs...) end -function compile_executable(funcs::Union{Array,Tuple}, path::String="./", name=fix_name(first(first(funcs))); +function compile_executable(funcs::Union{Array,Tuple}, path::String=pwd(), name=fix_name(first(first(funcs))); filename = name, demangle = true, cflags = ``, target::StaticTarget=StaticTarget(), + llvm_to_clang = Sys.iswindows(), kwargs... ) @@ -122,20 +124,20 @@ function compile_executable(funcs::Union{Array,Tuple}, path::String="./", name=f nativetype = isprimitivetype(rt) || isa(rt, Ptr) 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, kwargs...) + generate_executable(funcs, path, name, filename; demangle, cflags, target, llvm_to_clang, kwargs...) joinpath(abspath(path), filename) end """ ```julia -compile_shlib(f::Function, types::Tuple, [path::String="./"], [name::String=string(nameof(f))]; +compile_shlib(f::Function, types::Tuple, [path::String=pwd()], [name::String=string(nameof(f))]; filename::String=name, cflags=``, method_table=StaticCompiler.method_table, target::StaticTarget=StaticTarget(), kwargs...) -compile_shlib(funcs::Array, [path::String="./"]; +compile_shlib(funcs::Array, [path::String=pwd()]; filename="libfoo", demangle=true, cflags=``, @@ -172,7 +174,7 @@ julia> ccall(("test", "test.dylib"), Float64, (Int64,), 100_000) 5.2564961094956075 ``` """ -function compile_shlib(f::Function, types=(), path::String="./", name=fix_name(f); +function compile_shlib(f::Function, types=(), path::String=pwd(), name=fix_name(f); filename=name, target::StaticTarget=StaticTarget(), kwargs... @@ -180,7 +182,7 @@ function compile_shlib(f::Function, types=(), path::String="./", name=fix_name(f compile_shlib(((f, types),), path; filename, target, kwargs...) end # As above, but taking an array of functions and returning a single shlib -function compile_shlib(funcs::Union{Array,Tuple}, path::String="./"; +function compile_shlib(funcs::Union{Array,Tuple}, path::String=pwd(); filename = "libfoo", demangle = true, cflags = ``, @@ -289,10 +291,11 @@ function generate_executable(funcs::Union{Array,Tuple}, path=tempname(), name=fi demangle = true, cflags = ``, target::StaticTarget=StaticTarget(), + llvm_to_clang::Bool = Sys.iswindows(), kwargs... ) exec_path = joinpath(path, filename) - _, obj_path = generate_obj(funcs, path, filename; demangle, target, kwargs...) + _, obj_or_ir_path = generate_obj(funcs, path, filename; demangle, target, emit_llvm_only=llvm_to_clang, kwargs...) # Pick a compiler if !isnothing(target.compiler) cc = `$(target.compiler)` @@ -301,10 +304,10 @@ function generate_executable(funcs::Union{Array,Tuple}, path=tempname(), name=fi end # Compile! - if Sys.isapple() + if Sys.isapple() && !llvm_to_clang # Apple no longer uses _start, so we can just specify a custom entry entry = demangle ? "_$name" : "_julia_$name" - run(`$cc -e $entry $cflags $obj_path -o $exec_path`) + run(`$cc -e $entry $cflags $obj_or_ir_path -o $exec_path`) else fn = demangle ? "$name" : "julia_$name" # Write a minimal wrapper to avoid having to specify a custom entry @@ -319,9 +322,22 @@ function generate_executable(funcs::Union{Array,Tuple}, path=tempname(), name=fi return 0; }""") close(f) - run(`$cc $wrapper_path $cflags $obj_path -o $exec_path`) + 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 + elseif Sys.isapple() + `clang` + else + clang() + end + 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 - run(`rm $wrapper_path`) + rm(wrapper_path) end path, name end @@ -482,7 +498,6 @@ generate_obj(f, tt, path::String = tempname(), filenamebase::String="obj"; demangle = true, strip_llvm = false, strip_asm = true, - opt_level = 3, kwargs...) ``` Low level interface for compiling object code (`.o`) for for function `f` given @@ -519,10 +534,10 @@ end ```julia generate_obj(funcs::Union{Array,Tuple}, path::String = tempname(), filenamebase::String="obj"; target::StaticTarget=StaticTarget(), - demangle =false, + demangle = false, + emit_llvm_only = false, strip_llvm = false, strip_asm = true, - opt_level=3, kwargs...) ``` Low level interface for compiling object code (`.o`) for an array of Tuples @@ -534,25 +549,34 @@ This is a struct of the type StaticTarget() The defaults compile to the native target. """ function generate_obj(funcs::Union{Array,Tuple}, path::String = tempname(), filenamebase::String="obj"; + target::StaticTarget=StaticTarget(), demangle = true, + emit_llvm_only = false, strip_llvm = false, strip_asm = true, - opt_level = 3, - target::StaticTarget=StaticTarget(), kwargs...) f, tt = funcs[1] mkpath(path) - obj_path = joinpath(path, "$filenamebase.o") mod = static_llvm_module(funcs; demangle, kwargs...) - obj = GPUCompiler.JuliaContext() do ctx + + if emit_llvm_only # (Required on Windows) + ir_path = joinpath(path, "$filenamebase.ll") + open(ir_path, "w") do io + write(io, string(mod)) + end + return path, ir_path + else + obj_path = joinpath(path, "$filenamebase.o") + obj = GPUCompiler.JuliaContext() do ctx fakejob, _ = static_job(f, tt; target, kwargs...) obj, _ = GPUCompiler.emit_asm(fakejob, mod; strip=strip_asm, validate=false, format=LLVM.API.LLVMObjectFile) obj + end + open(obj_path, "w") do io + write(io, obj) + end + return path, obj_path end - open(obj_path, "w") do io - write(io, obj) - end - path, obj_path end end # module diff --git a/test/testcore.jl b/test/testcore.jl index e705ed6..064f010 100644 --- a/test/testcore.jl +++ b/test/testcore.jl @@ -50,6 +50,11 @@ end @test isa(r, Base.Process) @test r.exitcode == 0 + filepath = compile_executable(foo, (), workdir, llvm_to_clang=true) + r = run(`$filepath`); + @test isa(r, Base.Process) + @test r.exitcode == 0 + @inline function _puts(s::Ptr{UInt8}) # Can't use Base.println because it allocates Base.llvmcall((""" @@ -85,6 +90,11 @@ end @test isa(r, Base.Process) @test r.exitcode == 0 + filepath = compile_executable(print_args, (Int, Ptr{Ptr{UInt8}}), workdir, llvm_to_clang=true) + r = run(`$filepath Hello, world!`); + @test isa(r, Base.Process) + @test r.exitcode == 0 + # Compile a function that definitely fails @inline foo_err() = UInt64(-1)