Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instructions #2

Open
gabrielfreire opened this issue Jun 18, 2019 · 11 comments
Open

Instructions #2

gabrielfreire opened this issue Jun 18, 2019 · 11 comments

Comments

@gabrielfreire
Copy link

Hi @tshort ,

i love that you are putting some effort into making Julia standalone AOT a reality, could you explain to me how this would work and how could i make experiment with your implementation to better understand?

are you using a docker machine to work on this? if yes, could you share the Dockerfile ?

i would like to contribute as i would really appreciate to have standalone AOT that's not PackageCompiler huge in julia but i was looking at your commits and i'm kinda lost to be honest.

thank you

@tshort
Copy link
Owner

tshort commented Jun 19, 2019

On Linux, it's pretty easy. Just compile this branch from source, and you should be able to run the tests under test/standalone-mode/runtests.jl. These days, compiling from source runs pretty quickly. If you're not using Linux, you may try to do the same in a virtual machine or in Docker. If you want to run native on non-Linux, you'll probably have to adapt the scripts in test/standalone-mode.

For WebAssembly output, it's much more complicated, because you need Emscripten and need to compile a 32-bit version of Linux. I'm working on a Dockerfile for that.

@gabrielfreire
Copy link
Author

Thank you for your quick reply, do you intend to pull Julia from the master branch ? because i noticed that you are working on Julia code from 6 years ago, is updating the core julia code on your plans?

Do you intend to make a PR for this awesome feature to Julia ? it would be amazing if they could merge this.

I run on Windows, it should be very straightforward to build it in a Docker vm and customize runtests.jl to target dll/exe

I'm thinking on forking Julia #master to try and copy your implementation there, see what happens, any advice ?

Sorry for this bunch of questions, but i'm really excited, i even started learning more about c++ to see if i can do something to help you.

@tshort
Copy link
Owner

tshort commented Jun 19, 2019

My fork's master is many years old, but the standalone-mode-new branch is within a few weeks of master. I've been updating that branch to follow the jn/codegen-norecursion branch in the main Julia repo.

@tshort
Copy link
Owner

tshort commented Jun 19, 2019

See this PR.

@gabrielfreire
Copy link
Author

i even started learning more about c++ to see
Edit, C and C++ *

Ohh i didn't know about standalone-mode-new thank you for pointing me to the right direction, i was reading standalone-mode hehe, i was even a bit sad that the last update was 27 days ago.

Great stuff!

@gabrielfreire
Copy link
Author

gabrielfreire commented Jun 25, 2019

Hi @tshort, i managed to build your repository for Windows using Cygwin and following the instructions on doc/build/windows.md, the build was successful and i managed to generate a Julia binary. Now i'm trying to execute the standalone-aot tests, so i replace all .o for .a and all .so for .dll

but i'm getting this error when i run

gabriel.freire@gfreire-m1 ~/julia/test/standalone-aot/standalone-exe
$ /home/gabriel.freire/julia/julia-win64/usr/bin/julia.exe compile.jl

Error

$ /home/gabriel.freire/julia/julia-win64/usr/bin/julia.exe compile.jl
┌ Warning: The two argument form of `func_for_method_checked` is deprecated. Pass sparams in addition.
│   caller = irgen(::Any, ::Any) at IRGen.jl:21
└ @ Main.IRGen C:\cygwin64\home\gabriel.freire\julia\test\standalone-aot\IRGen.jl:21
clang version 6.0.1 (tags/RELEASE_601/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\cygwin64\home\gabriel.freire\julia\julia-win64\usr\tools
 "C:\\cygwin64\\bin\\ld.exe" -m i386pep --shared -Bdynamic -e DllMainCRTStartup --enable-auto-image-base -o libtwox.dll dllcrt2.o "C:\\cygwin64\\lib\\gcc\\x86_64-w64-mingw32\\6.4.0\\crtbegin.o" "-LC:\\cygwin64\\lib\\gcc\\x86_64-w64-mingw32\\6.4.0" "-LC:\\cygwin64\\x86_64-w64-mingw32\\lib" "-LC:\\cygwin64\\lib" "-LC:\\cygwin64\\x86_64-w64-mingw32/sys-root/mingw/lib" libtwox.a -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt "C:\\cygwin64\\lib\\gcc\\x86_64-w64-mingw32\\6.4.0\\crtend.o"
/usr/bin/ld: cannot find dllcrt2.o: No such file or directory
/usr/bin/ld: cannot find -lmingw32
/usr/bin/ld: cannot find -lmoldname
/usr/bin/ld: cannot find -lmingwex
/usr/bin/ld: cannot find -lmsvcrt
/usr/bin/ld: cannot find -lmingw32
/usr/bin/ld: cannot find -lmoldname
/usr/bin/ld: cannot find -lmingwex
/usr/bin/ld: cannot find -lmsvcrt
clang.exe: error: linker command failed with exit code 1 (use -v to see invocation)
ERROR: LoadError: failed process: Process(`'C:\cygwin64\home\gabriel.freire\julia\julia-win64\usr\tools/clang' -v -shared -fpic libtwox.a -o libtwox.dll`, ProcessExited(1)) [1]

Stacktrace:
 [1] pipeline_error at .\process.jl:819 [inlined]
 [2] #run#545(::Bool, ::typeof(run), ::Cmd) at .\process.jl:734
 [3] run(::Cmd) at .\process.jl:732
 [4] top-level scope at C:\cygwin64\home\gabriel.freire\julia\test\standalone-aot\standalone-exe\compile.jl:22
 [5] include at .\boot.jl:328 [inlined]
 [6] include_relative(::Module, ::String) at .\loading.jl:1094
 [7] include(::Module, ::String) at .\Base.jl:31
 [8] exec_options(::Base.JLOptions) at .\client.jl:295
 [9] _start() at .\client.jl:464
in expression starting at C:\cygwin64\home\gabriel.freire\julia\test\standalone-aot\standalone-exe\compile.jl:19

This is my Clang version

$ ../../../julia-win64/usr/tools/clang -v
clang version 6.0.1 (tags/RELEASE_601/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\cygwin64\home\gabriel.freire\julia\julia-win64\usr\tools

compile.jl

include("../IRGen.jl")
using .IRGen

twox(x) = 2x
arraysum(x) = sum([x, 1])
myrand() = rand()

funcs = [
    "twox" => (twox, Tuple{Int}),
    "arraysum" => (arraysum, Tuple{Int}),
    "myrand" => (myrand, Tuple{}),
]

dir = @__DIR__
bindir = joinpath(abspath(Sys.BINDIR, ".."), "tools")
libdir = joinpath(abspath(Sys.BINDIR, ".."), "lib")
include_juliadir = joinpath(abspath(Sys.BINDIR, ".."), "include", "julia")

for (fname, (func, sig)) in funcs
    native = irgen(func, sig)
    dump_native(native, "lib$fname.a")
    run(`$bindir/clang -v -shared -fpic lib$fname.a -o lib$fname.dll`)
    run(`$bindir/clang -v -c -std=gnu99 -I'$include_juliadir' -DJULIA_ENABLE_THREADING=1 -fPIC $fname.c`)
    run(`$bindir/clang -v -o $fname $fname.a -L$dir -L$libdir -Wl,--unresolved-symbols=ignore-in-object-files -Wl,-rpath,'.' -Wl,-rpath,'$libdir' -ljulia-debug -ldSFMT -l$fname`)
end

If you could help please.

@tshort
Copy link
Owner

tshort commented Jun 27, 2019

That's an ambitious attempt. You got farther than I thought. You might need to look at the libdir variable and/or the -L and -l flags.

As a heads up, it looks like this branch is a dead end towards static compilation. See the PR on Julia (now closed).

@gabrielfreire
Copy link
Author

I ended up setting up a docker machine with ubuntu and had a lot of fun generating small executables for Julia functions.

As a heads up, it looks like this branch is a dead end towards static compilation. See the PR on Julia (now closed).

Oh no :(, that's sad, i had a lot of fun with your implementation, but hopefully this means they are close to a tree-shaking solution, at the moment PackageCompiler only generates huge executables along with huge .dll/.so.

I could be of more help if i knew more C or C++, but most people tell me i shouldn't waste time learning those and instead try to learn Rust or Go. i really have no idea what to do coming from a
more high-level javascript/python/java/c# background with little knowledge of C that i got in University.

I also believe that the only thing stopping Julia from competing with System programming languages like C++/Rust/D/Go/Swift is the ability of generating static binaries. (i could be completely wrong here, let me know please)

I see you are a C/C++ expert, so what would you advise ? Specially now that Microsoft has .NET Core 3 open-sourced and cross-platform, but anyway, C# can't really be compared to C++ in terms of performance of compiler generated code, i think C++ has the best compiler ever.

This language thing is a cross-roads, can't decide, i think i'll stick with C++ just for the sake of being more able to understand what's going on in Julia's sources, i gave it a good read and i was impressed on how much of it i could already make sense of, so i believe it shouldn't be too much of a pain learning C++, and about C... they are very similar anyway, considering C++ is much bigger and has many more features of course.

But i'd love to hear your thoughts/advice on my text full of random stuff.

@tshort
Copy link
Owner

tshort commented Jun 27, 2019

I'm definitely not a C/C++ expert. I mainly find it painful. I'm an engineer, not a software developer. Languages I'm most comfortable with are first Julia, then R, then Python, then JavaScript, and C/C++ is last.

I've thought about learning Rust because it has great WebAssembly support. I'm mainly interested in converting web apps (here) from JavaScript to Julia, but Rust is an interesting alternative. Rust isn't great at scientific computing, but it's getting better.

As to whether you should learn C/C++, it really depends on your career goals. If you are looking for software development jobs, C/C++ is a great skill--there are tons of jobs in a wide range of fields in a wide range of physical locations. Those are not going away anytime soon. Something like Go or Rust is much more niche. If you are looking for engineering or scientific positions, I'd stick with high-level languages--it'd be good to have passing knowledge of C/C++ so you can read code, but you don't need to be an expert.

@gabrielfreire
Copy link
Author

Thank you for your reply.

I'm a software developer, i use mainly Java, C#, Python and Javascript/TypeScript at work and i'm mainly interested in web development, i think this is my main barrier towards C++, everybody says that writing web services in C++ is a nightmare, i think i'll give a chance to Rust and become at least capable of writing simple software in C++, my main interest is Web, Machine Learning and small CLI tools for automation. The only thing that's left for me in Julia is small binary executable generation, and this is what's attractive for me in C++.

@tshort
Copy link
Owner

tshort commented Jun 27, 2019

As a software developer, it's probably worth learning both Rust and C++. How deep you go is an open question and depends on your needs and interests. Rust seems quite good for web and CLI but lags for machine learning.

Keno added a commit that referenced this issue Jan 17, 2020
…#32605)

The bug here is a bit subtle, but perhaps best illustrated with
the included test case:
```
function f32579(x::Int64, b::Bool)
    if b
        x = nothing
    end
    if isa(x, Int64)
        y = x
    else
        y = x
    end
    if isa(y, Nothing)
        z = y
    else
        z = y
    end
    return z === nothing
end
```
The code just after SSA conversion looks like:
```
2  1 ─       goto JuliaLang#3 if not _3
3  2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
5  3 ┄ %3  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
   │   %4  = (%3 isa Main.Int64)::Bool
   └──       goto JuliaLang#5 if not %4
6  4 ─ %6  = π (%3, Int64)
   └──       goto JuliaLang#6
8  5 ─ %8  = π (%3, Nothing)
10 6 ┄ %9  = φ (JuliaLang#4 => %6, JuliaLang#5 => %8)::Union{Nothing, Int64}
   │   %10 = (%9 isa Main.Nothing)::Bool
   └──       goto JuliaLang#8 if not %10
11 7 ─ %12 = π (%9, Nothing)
   └──       goto JuliaLang#9
13 8 ─ %14 = π (%9, Int64)
15 9 ┄ %15 = φ (JuliaLang#7 => %12, JuliaLang#8 => %14)::Union{Nothing, Int64}
   │   %16 = (%15 === Main.nothing)::Bool
   └──       return %16
```
Now, we have special code in SROA (despite it not really being an
SROA transform) that looks at `===` and replaces
it by a nest of phis of booleans. The reasoning for this transform
is that it eliminates a use of a value where we only care about the
type and not the content, thus making it more likely that the value
will subsequently be eligible for SROA. In addition, while it goes
along resolving which values feed into any particular phi, it
accumulates and type conditions it encounters along the way.

Thus in the example above, something like the following happens:
- We look at %14, which πs to %9 with an Int64 constraint, so we only
  consider the JuliaLang#4 predecessor for %9 (due to the constraint), until
  we get to %3, where we again only consider the #1 predecessor,
  where we find the argument (of type Int64) and conclude the result
  is always false
- Now we pop the next item of the stack from our original phi, look
  at %12, which πs to %9 with a Nothing constraint.

At this point we used to terminate the search because we already looked
at %9. However, crucially, we looked at %9 only with an Int64 constraint,
so we missed the fact that `nothing` was in fact a possible value for this
phi. The result was a missing entry in the generated phi node:
```
1 ─       goto JuliaLang#3 if not b
2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
3 ┄ %3  = φ (#1 => false)::Bool
│   %4  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
│   %5  = (%4 isa Main.Int64)::Bool
└──       goto JuliaLang#5 if not %5
4 ─ %7  = π (%4, Int64)
└──       goto JuliaLang#6
5 ─ %9  = π (%4, Nothing)
6 ┄ %10 = φ (JuliaLang#4 => %3, JuliaLang#5 => %3)::Bool
│   %11 = φ (JuliaLang#4 => %7, JuliaLang#5 => %9)::Union{Nothing, Int64}
│   %12 = (%11 isa Main.Nothing)::Bool
└──       goto JuliaLang#8 if not %12
7 ─       goto JuliaLang#9
8 ─       nothing::Nothing
9 ┄ %16 = φ (JuliaLang#7 => %10, JuliaLang#8 => %10)::Bool
└──       return %16
```
(note the missing #2 predecessor in phi node %3), which would result
in an undefined value at runtime, though in this case LLVM would
have taken advantage of that to just return 0:
```
define i8 @julia_f32579_16051(i64, i8) {
top:
;  @ REPL[1]:15 within `f32579'
  ret i8 0
}
```
Compare this now to the optimized IR with this patch:
```
1 ─       goto JuliaLang#3 if not b
2 ─ %2  = Main.nothing::Core.Compiler.Const(nothing, false)
3 ┄ %3  = φ (#2 => true, #1 => false)::Bool
│   %4  = φ (#2 => %2, #1 => _2)::Union{Nothing, Int64}
│   %5  = (%4 isa Main.Int64)::Bool
└──       goto JuliaLang#5 if not %5
4 ─ %7  = π (%4, Int64)
└──       goto JuliaLang#6
5 ─ %9  = π (%4, Nothing)
6 ┄ %10 = φ (JuliaLang#4 => %3, JuliaLang#5 => %3)::Bool
│   %11 = φ (JuliaLang#4 => %7, JuliaLang#5 => %9)::Union{Nothing, Int64}
│   %12 = (%11 isa Main.Nothing)::Bool
└──       goto JuliaLang#8 if not %12
7 ─       goto JuliaLang#9
8 ─       nothing::Nothing
9 ┄ %16 = φ (JuliaLang#7 => %10, JuliaLang#8 => %10)::Bool
└──       return %16
```
The %3 phi node has its missing entry and the generated LLVM code
correctly returns `b`:
```
define i8 @julia_f32579_16112(i64, i8) {
top:
  %2 = and i8 %1, 1
;  @ REPL[1]:15 within `f32579'
  ret i8 %2
}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants