Three points to keep in mind here:
static
variables into functions persist through the entire duration of the program as soon as they're created for the first time
That variable returned also has the postfix ++ operator which means: "use the value (i.e. return it) and increment it AFTERWARDS": the incremented value is not returned.
That's why that variable has "memory" of what happened and gets incremented.
-> Why you're seeing "3 2 1" instead of "1 2 3"?
The order in which the parameters are evaluated is not known 'a priori' and it's up to the compiler decide it, see https://stackoverflow.com/a/12960263/1938163
If you really want to know how is it possible that the value gets first returned and then incremented, take a look at the generated asm code:
demo(): # @demo()
movl demo()::i, %eax # move i and put it into eax
movl %eax, %ecx # Move eax into ecx -> eax will be used/returned!
addl $1, %ecx # Increment ecx
movl %ecx, demo()::i # save ecx into i -> this is for the next round!
ret # returns!
main: # @main
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $0, -4(%rbp)
callq demo() # Call demo()
movl %eax, -8(%rbp) # save eax in rbp-8 (contains 1, demo::i is 2 for the next round)
callq demo() # Call demo()
movl %eax, -12(%rbp) # save eax in rbp-12 (contains 2, demo::i is 3 for the next round)
callq demo() # Call demo()
leaq .L.str, %rdi # load str address
movl -8(%rbp), %esi # esi points to 1
movl -12(%rbp), %edx # edx points to 2
movl %eax, %ecx # move eax (3) into ecx (demo::i is 4 but doesn't get used)
movb $0, %al # needed by the ABI to call printf
callq printf # call printf() and display 3 2 1
movl $0, %ecx
movl %eax, -16(%rbp)
movl %ecx, %eax
addq $16, %rsp
popq %rbp
ret
demo()::i:
.L.str:
.asciz "%d %d %d\n"
The 64-bit ABI uses registers (RDI, RSI, RDX, RCX, R8 and R9) instead of the stack for argument passing.