Reset/reuse & morphic
My goal here is to be able to do reset/reuse without checking if the refcount is 1 at runtime.
Our reset/reuse behave like in the "counting immutable beans" paper.
reset is like a dec with extra behavior: If the refcount was 1, it will not actually free the memory, but write a pointer to the memory to some known location. If the refcount is not 1, NULL is written to that location, and a normal dec is performed.
reuse then looks at this saved pointer location: if it is NULL, it must reallocate new memory, otherwise it will reuse the memory at the address to store a new tag value.
So then the question is how to model this. I added an UpdateMode to connect the Reset and Reuse. I think the "can we update in-place" check should happen in the Reset.
We now store recursive tag unions as ( heap_cell, union { ... } ) wrapped in a name. We decompose all that, then try to perform an update on the union { ... } value, then make a tuple with a new heap cell and the union { ... } value.
// unwrap the named wrapper
let union_id = builder.add_unwrap_named(block, MOD_APP, type_name, tag_value_id)?;
// decompose the ( heap_cell, union { ... } ) tuple
let heap_cell = builder.add_get_tuple_field(block, union_id, TAG_CELL_INDEX)?;
let union_data = builder.add_get_tuple_field(block, union_id, TAG_DATA_INDEX)?;
let mode = update_mode.to_bytes();
let update_mode_var = UpdateModeVar(&mode);
// try to do an update, so the update_mode can later tell us if we can update in-place
let _unit = builder.add_update(block, update_mode_var, heap_cell)?;
with_new_heap_cell(builder, block, union_data)
Now, this gives results
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = InPlace
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = InPlace
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = InPlace
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = InPlace
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = InPlace
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
[compiler/gen_llvm/src/llvm/build.rs:1121] update_mode = Immutable
But when I skip the refcount check for InPlace cases, we get errors in valgrind. So most likely our specification is incorrect, leading to incorrect results from morphic, leading to invalid memory access.
Figured out that the issue with Derive.roc is due to a problem with continuations. It's unclear whether it's an issue with our encoding or with morphic itself.
issue on the morphic repo: https://github.com/morphic-lang/morphic_lib/issues/17
@folkertdev Is this performance improvement still desirable?
yes
this was implemented as https://github.com/roc-lang/roc/pull/4526