About Julia's stack (its range, growing direction, etc)

I want to know if a certain object is allocated on stack or heap, here’s my thought:

  • find the range of the stack
  • find the location where the object data is placed
  • check if the data location is in the stack range

And here is what I did:

#include "julia.h"

JL_DLLEXPORT void jl_obj_inspect(jl_value_t *obj)
{
    int i = 0;
    jl_ptls_t ptls = jl_get_ptls_states();
    char *stackbase = (char*)ptls->stackbase;

    printf("stacksize        = %ld\n", ptls->stacksize);
    printf("offset of i      = %ld\n", (char*)(&i) - stackbase);
    printf("offset of obj    = %ld\n", (char*)obj - stackbase);
    if(jl_is_int64(obj)) {
        printf("int value        = %ld\n", *(long*)obj);
    }
    printf("-------------\n");
}

lib = Libdl.dlopen_e("./test.so")
obj_inspector = dlsym(lib, :jl_obj_inspect)

a0 = [1 2; 3 4] # an array
ccall(obj_inspector, Cvoid, (Any,), a0)

struct S
    i::Int
end
s0 = S(1) # an immutable obj
ccall(obj_inspector, Cvoid, (Any,), s0)
s1 = S(1) # an immutable obj
ccall(obj_inspector, Cvoid, (Any,), s1)

d = 1 # a primitive
ccall(obj_inspector, Cvoid, (Any,), d)
d = 2 # a primitive
ccall(obj_inspector, Cvoid, (Any,), d)

And below is the output:

stacksize        = 4194304
offset of i      = 765695344180
offset of obj    = 14176656
-------------
stacksize        = 4194304
offset of i      = 765695344180
offset of obj    = 14257248
-------------
stacksize        = 4194304
offset of i      = 765695344180
offset of obj    = 14258672
-------------
stacksize        = 4194304
offset of i      = 765695344180
offset of obj    = 299104
int value        = 1
-------------
stacksize        = 4194304
offset of i      = 765695344180
offset of obj    = 299168
int value        = 2
-------------

What I saw shows:

  • the stack size is 4194304 (4M)
  • ptls->stackbase is the lowest address of the stack, so the stack range is (stackbase, stackbase+stacksize), stack grows from high addr to low addr
  • int values are allocated on stack (offset < stacksize)
  • mutable objects are allocated on heap (offset > stacksize)
  • [?] immutable objects are allocated on heap (offset > stacksize)
  • [?] c code (called via ccall) and Julia code use different stacks (the offset of int i is far far away from the stackbase)

I am not sure if my observation is correctly explained, especially the ones marked with question marks, I would very appreciate it if someone could clarify them, thank you.

No. it’s the highest address of a different stack. You are checking the wrong range and this range has nothing to do with the current stack…

No.

No, you are observing the C API, it has nothing to do with object allocations.

Same here.

No.

These are not something that can be observed and basically none of the questions you are asking about can be answered by writing any code.

All objects can be allocated anywhere. The pointer you get in C code using Any are always boxed value and are never stack allocated but it may or may not has anything to do with the object being used in julia code.

Thank you.

I am still wondering what’s usage of the stack indicated by ptls->stackbase and ptls->stacksize.

Thank you very much for pointing out this, it helps me a lot to understand the code.

Is it possible to get some information about memory allocation from reading the lowered code or generated LLVM-IR?

It’s a stack used by other tasks.

Yes. Some of the allocation elimination can be seen in the julia IR and some other ones are done at LLVM level.

It may help to clarify things that Julia doesn’t have a single stack. Each task has its own stack. In fact, for me the simplest way to understand coroutines (i.e. tasks) is that each one is an independent call stack and that they can transfer execution back and forth between each other with yieldto.

3 Likes