In the first procedure, I get a user by pointer for updating; in the second, by reference for reading.
When I dereference the user to pass it to the user_get_age procedure, will Odin make a copy of the user or just pass a reference?
User :: struct {
age: int,
}
user_set_age :: proc(u: ^User, age: int) {
u.age = age
}
user_get_age :: proc(u: User) -> int {
return u.age
}
main :: proc() {
user := new(User)
user_set_age(user, 2)
user_get_age(user^)
}
This question arose because I know that if I pass the user directly to user_get_age , it will not be copied but simply passed by reference. Copying only occurs if I do something like user := user inside user_get_age . However, I’m unsure what happens when I pass a dereferenced user.
main :: proc() {
user := User{}
user_set_age(&user, 2)
user_get_age(user)
}
In a more general sense, it will depend on the calling convention for the function. Unless specified otherwise, like in your example, a procedure will default to odin:
odin - default convention used for an Odin proc. It passes all parameters larger than 16 bytes by reference and passes an implicit context pointer on each call. (Note: This is subject to change)
This suggests to me that the procedure user_get_age will pass user by copying the value as opposed to by passing a reference due to User being ~8 bytes in size (depending on certain platform details).
Thank you for your answer!
Ok, let’s imagine that the user is a large structure with a size greater than 16 bytes. In my opinion, when we dereference the user, we copy it to the stack and send a reference of user copy to user_get_age.
I made some modifications to my example and compiled it to assembly for better understanding. I used contextless procedures to generate clearer assembly without passing extra arguments.
user_set_age :: proc "contextless" (u: ^User, age: int) {
u.age = age
}
user_get_age :: proc "contextless" (u: User) -> int {
return u.age
}
main :: proc() {
user := new(User)
user_set_age(user, 2)
user_get_age(user^)
}
When the User struct is 16 bytes or smaller, Odin copies the entire structure:
User :: struct {
age: int,
_: int,
} // 16 byte
movq 32(%rsp), %rcx
movq (%rcx), %rax
movq 8(%rcx), %rcx
movq %rcx, 24(%rsp)
movq %rax, 16(%rsp)
movq 16(%rsp), %rdi
movq 24(%rsp), %rsi
callq "main::user_get_age"@PLT
However, when the struct exceeds 16 bytes, Odin simply passes a pointer:
User :: struct {
age: int,
_: int,
_: int,
_: int,
} // 32 byte
movq 16(%rsp), %rdi
callq "main::user_get_age"@PLT