A Case for a Native Runtime Compilation Language(jott.live)
jott.live
A Case for a Native Runtime Compilation Language
https://jott.live/markdown/dynamic_compilation
5 comments
Lisp?
Isn’t this roughly what Julia does?
yes! Julia does compile this way. Julia emphasizes JIT compilation based on runtime types rather than runtime values. I couldn't get runtime captures to work, e.g.
a = 2
function f(n)
return n * a
end
println(f(4))
@code_llvm f(4)
@code_native f(4)That's because defining `a = 2` at the top level is a global constant, which can be changed at any time. Hence, the compiler can't optimize it out. If you tell the compiler it's a `const` though, it'll indeed do the optimization you were hoping for
const a = 2
f(n) = n * a
julia> @code_llvm f(4)
; @ REPL[2]:1 within `f'
define i64 @julia_f_290(i64 signext %0) {
top:
; @ REPL[2]:2 within `f'
; ┌ @ int.jl:88 within `*'
%1 = shl i64 %0, 1
; └
ret i64 %1
}
julia> @code_native f(4)
.text
; ┌ @ REPL[2]:2 within `f'
; │┌ @ int.jl:88 within `*'
leaq (%rdi,%rdi), %rax
; │└
retq
nopw %cs:(%rax,%rax)
; └Julia does this. The only caveat is that if you write
Hence, one way to do this would be to write
Here's an example at the repl where we can inspect the generated code more easily:
function foo(a, xs)
function bar(b)
b * a
end
res = 0
for x in xs
res += bar(x)
end
res
end
what the author desires (the lifting of a to a compile time constant) won't happen automatically because compiling a new bar for every value of a is going to be a bad idea unless the loop over xs takes longer than compiling a function. Hence, we make people 'opt-in' to this by lifting a from a runtime variable to a type parameter (julia types are parametric and their parameters can contain values).Hence, one way to do this would be to write
foo(a, xs) = foo(Val{a}, xs)
function foo(::Type{Val{a}}, xs) where {a}
function bar(b)
b * a
end
res = 0
for x in xs
res += bar(x)
end
res
end
Now, when you run foo(2, v) (where v is some iterable), this will turn into foo(Val{2}, v) which then gets specialized on the type parameter {2}, and then can compile a new bar function that's aware of the value of a.Here's an example at the repl where we can inspect the generated code more easily:
julia> f(a) = f(Val{a})
f (generic function with 1 method)
julia> function f(::Type{Val{a}}) where {a}
function g(b)
b * a
end
end
f (generic function with 2 methods)
julia> g1 = f(1)
(::var"#g#4"{1}) (generic function with 1 method)
julia> @code_llvm debuginfo=:none g1(4)
define i64 @julia_g_442(i64 signext %0) {
top:
ret i64 %0
}
julia> g2 = f(2)
(::var"#g#4"{2}) (generic function with 1 method)
julia> @code_llvm debuginfo=:none g2(4)
define i64 @julia_g_444(i64 signext %0) {
top:
%1 = shl i64 %0, 1
ret i64 %1
}
Notice that g1 and g2 have different optimizations applied that depend on the different runtime values of a.