How does the compiler decide which code to eliminate here? Thanks for the answer!
I bring this example because both foo2 and foo3 (edit: should) have apparently observable effects (OOM and ArgumentError, resp.), yet the behaviour is different.
OutOfMemoryError is an error that isn’t modeled (i.e. the 2nd example runs if you have enough RAM). The difference between foo2 and foo3 is that foo3 errors, while foo2 is just sometimes run on a computer without good enough specs.
The only other error Jeff and I can think of that isn’t modeled is StackOverflow. (The reasoning is that ~any code theoretically can throw those, and they don’t exist within the Turing machine model of a machine).
Perhaps also InterruptException, in some sense? Say,
function foo6()
try
# Expected to run for a long time
for i in 1:2^30 end
catch
# We hope this runs when Ctrl + C is pressed
println("Interrupted, printing useful stats: ...")
end
end
julia> @code_native debuginfo=:none foo6()
.text
.file "foo6"
.section .ltext,"axl",@progbits
.globl julia_foo6_2370 # -- Begin function julia_foo6_2370
.p2align 4, 0x90
.type julia_foo6_2370,@function
julia_foo6_2370: # @julia_foo6_2370
; Function Signature: foo6()
# %bb.0: # %top
push rbp
mov rbp, rsp
xor eax, eax
pop rbp
ret
.Lfunc_end0:
what happens at size 252 that makes the allocation stick? It’s also a weird cutoff value, not being an exact power of 2, or an adjacent number (although it’s nearby, for some definition of closeness)
the thing that happens is the allocation moves from being a pool allocation to a malloc (the weird size is that the number of bytes for the Memory is 16+8*elsize), and dealing with that case would have required me to write another ~50 lines of LLVM to model the other allocation path, and I want to get an initial version of this merged and go from there.
A layman question: if I understand right, the PR eagerly evaluates the allocation, sometimes. For eliminating the allocation and the store in foo4, should there be a more high-level mechanism? Say, since the array is local to that function, and there are no loads from the array, eliminate the stores (which are dead anyway), and then eliminate the allocation ?
you’re just seeing LLVM performing the proof of inboundsness. the reason why the memorynew pr is needed is LLVM doesn’t know that jl_alloc_genericmemory doesn’t leak the array it creates since it’s an arbitrary C function