On Thu, Sep 22, 2016 at 8:44 AM, Jamie Brandon
<ja...@scattered-thoughts.net> wrote:
> Well, I managed to answer the first part of my question. A variable won't be
> stack-allocated unless the compiler can prove it is always defined before
> being used. Mine were, but the control flow was too complex for the compiler
> to deal with. I added `local finger = Finger{1}(0,0)` in a few places to
> make life easier for the compiler and my allocations went away.
>
> I'm still interested in the second half of the question though - is there a
> better way to debug allocation problems other than guesswork?

For most cases code_warntype should be able to tell you that. The
use-before-assign is one of the few cases that's not captured by it.
(Other cases are mostly due to the compiler heuristics specializing on
types/functions). Ideally, use-before-assign issue should be fixed and
avoid having to box the variable.

>
> On 22 September 2016 at 14:06, Jamie Brandon <ja...@scattered-thoughts.net>
> wrote:
>>
>> Oh, for comparison, this simper function contains no heap allocation at
>> all, so the compiler is definitely willing to do this under some
>> circumstances.
>>
>> function foo()
>>   finger = Finger{1}(1,1)
>>   for _ in 1:1000
>>     finger = Finger{1}(finger.hi + finger.lo, finger.lo)
>>   end
>>   finger
>> end
>>
>> On 22 September 2016 at 14:01, Jamie Brandon
>> <ja...@scattered-thoughts.net> wrote:
>>>
>>> I have a query compiler which emits Julia code. The code contains lots of
>>> calls to generated functions, which include sections like this:
>>>
>>>     hi = gallop(column, column[finger.lo], finger.lo, finger.hi, <=)
>>>     Finger{$(C+1)}(finger.lo, hi)
>>>
>>> Finger is an immutable, isbits type:
>>>
>>>   immutable Finger{C}
>>>     lo::Int64
>>>     hi::Int64
>>>   end
>>>
>>> When I run the generated code I see many millions of allocations. Using
>>> code_warntype I can see that all the generated functions have been inlined,
>>> every variable has a concrete inferred type and there are no generic calls.
>>> And yet I see many sections in the llvm code like this:
>>>
>>>   %235 = call %jl_value_t* @jl_gc_pool_alloc(i8* %ptls_i8, i32 1456, i32
>>> 32)
>>>   %236 = getelementptr inbounds %jl_value_t, %jl_value_t* %235, i64 -1,
>>> i32 0
>>>   store %jl_value_t* inttoptr (i64 139661604385936 to %jl_value_t*),
>>> %jl_value_t** %236, align 8
>>>   %237 = bitcast %jl_value_t* %235 to i64*
>>>   store i64 %229, i64* %237, align 8
>>>   %238 = getelementptr inbounds %jl_value_t, %jl_value_t* %235, i64 1
>>>   %239 = bitcast %jl_value_t* %238 to i64*
>>>   store i64 %234, i64* %239, align 8
>>>   store %jl_value_t* %235, %jl_value_t** %finger_2_2, align 8
>>>   %.pr = load %jl_value_t*, %jl_value_t** %finger_1_2, align 8
>>>
>>> The pointer on the third line is:
>>>
>>>   unsafe_pointer_to_objref(convert(Ptr{Any}, 139661604385936))
>>>   # => Data.Finger{1}
>>>
>>> So it appears that these fingers are still being heap-allocated.
>>>
>>> What could cause this? And more generally, how does one debug issues like
>>> this? Is there any way to introspect on the decision?
>>
>>
>

Reply via email to