Allocating objects with NEW initializes the object state and sets all object variables to 0/0.0/null. When performing load elimination (where we match up stores into captured objects with subsequent loads), we previously didn’t eliminate these types of loads because they don’t have a matching store. I changed the load elimination pass to properly replace these loads with a CONSTANT instruction.
With this latest change all captured allocations should no longer be loaded from as long we can properly analyze them. We can’t always analyse array accesses, i.e. if the index used to access the array is variant. As long the latter is not the case, captured allocation sites will only be pointed to by stores, no reads. This means that if the allocation is singleton, we can actually hoist it out of the loop.
Previously we weren’t able to do this, because loads of initial values would no longer be guaranteed. If an initial value is loaded in the first iteration, the proper initial value would be returned. If the loop changes the object state, however, subsequent iterations would see this new value. Since we resolve such initial loads into a proper constant now, this is no longer an issue.
Its important to note that we can’t hoist all captured allocations out of the loop, only those that are singleton, which means that only one copy of the object is alive during a loop iteration. This is actually not that hard to check. In forward order we go over the tree and check that the context slot the allocation flowed into is only used until the actually allocation occurs, after which point only this new reference must be used (including side exits). If thats the case the allocation is singleton: any leftover reference to the allocation from the previous loop iteration is replaced by the allocated object of the current loop iteration.
The singleton check is not implemented yet. Once that is in place, hoisting of object allocations should finally be safe.