Does Odin implement Return Value Optimization (RVO)?

Like in C++, I can put print statements in constructors/destructors and track, if there are new copies created, when returning local variable from function (Copy elision - Wikipedia).

But I don’t know, if it happens, and don’t know, how to implement this in Odin.

Maybe someone have some information on this subject?

There are no constructors or destructors in Odin, just new/free and make/delete, which are the core of manual memory management in the language. Any value you see outside of those conventions exists on the stack (a: i32, for example). Even a struct has no constructor; if you have defined b: Big_Struct, then that too is on the stack unless heap allocated by one of the memory procedures.

By default, all memory is zero-initialized; there is no way to set any other default value to fields on a complex datatype, as the zero initialization is a property of the Odin memory allocator API.

To put this into persective and better understand your situation, do you have some example code you’re concerned about?

1 Like

Yes, I understand all that.

Example would be:

getBigStruct :: proc(x: i32) -> BigStruct {
    b: BigStruct
    b.a = someHeavyProc1(x)
    b.b = someHeavyProc2(b.a)
    ...
    return b
}

And after that:

bOutside := getBigStruct(5)

In this example, how many times is b copied on stack?

It is copied once because of the return, unless the optimizer inlines getBigStruct.

Both of those methods might be exaclty the methods you don’t want to use, but here goes: If you want to absolutely avoid copying on the stack then 1. you can simply create the variable outside, pass by pointer and do the calculations in getBigStruct or 2. allocate inside getBigstruct and return a pointer.

EDIT: I might have misunderstood you. But If I’m not mistaken, then RVO is partially a means to prevent stupid things that come with OOP’s constructors. So this feature solves a problem that doesn’t exist in the same way in a procedural language. Odin often has create_* (these usually allocate) and init_* (these usually take a pointer and don’t allocate) procedures for a lot of things in its standard library, because it often makes sense to create an empty variable and do initialization using a pointer. All that said, Odin uses LLVM and its optimizer, so whatever RVO happening in C++ when using the Clang compiler, also happens to Odin’s code after it is translated to LLVM’s IR code, because that is what the optimizer actually works with (this is still simplified). EDIT: Strike bad infos, thanks for the correction ratchetfreak!

RVO is more of a front-end thing because it allows the front end to in-place construct into the return value pointer instead of constructing a temporary and then copy or move constructing it into the final location and then destructing the original.

This removes an entire set of function calls which could invoke arbitrary user code so LLVM (which is generally a semantics-preserving optimizer) cannot do that transform. And it’s also why it (and NRVO) are mentioned explicitly in the spec along other copy elisions because it removes the need to call the constructor and strictly speaking violates the as-if rule.

1 Like

Your question’s made it into the official FAQ: Frequently Asked Questions | Odin Programming Language

1 Like