SymbolReplacedOrDeleted had an "emergency fix" that disabled several kinds of substitutions, because they generated code that didn't compile. The cause was actually elsewhere.
The actual problem was the marking of function parameters as being written to by function calls. This is true in a sense, but there's a big scope change that totally destroys the possibility of substituting identifiers, for example.
We were not removing the function parameters, anyway, therefore that code has just been disabled.
Note that removal of function parameters may be impossible if one parameter has side effects. Consider this:
f(string x, integer y, string z)
{
llOwnerSay(x + z);
}
integer n = 2;
default{state_entry(){
f("a" + (string)n, n=llSetRegionPos(<100,100,100>), "c" + (string)n);
}}
Even worse if the expression for the x argument has side effects too and x and y need to be performed in the right order.
Fortunately, such case is highly unlikely. But if we ever implement removal of function parameters, that's an additional difficulty to take care of.
This allows optimizing, for example:
integer a = 1;
integer b = a;
llOwnerSay((string)b);
which wasn't done before. This case is prone to happen with inlined functions, e.g. using the result of an inlined function as a parameter to another.
It was an awful name but we couldn't think of anything better. That's what we have come up with as a substitute, which is not entirely accurate but it is MUCH more descriptive of what it actually does.
The missing bit was to mark labels are SEF when they are not referenced. Label references are now counted at parse time, enabling us to do this.
Also, make FoldStmt clearer when the node is an expression.
That was long overdue. Obviously, this is a large commit.
The new nr (node record) class has built-in dump capabilities, rather than using print_node().
SEF always exists now, and is a boolean, rather than using the existence of SEF as the flag. This was changed for sanity. However, other flags like 'X' are still possibly absent, and in some cases the absence itself has meaning (in the case of 'X', its absence means that the node has not yet been analyzed).
Similarly, an event is distinguished from a UDF by checking for the existence of the 'scope' attribute. This trick works because events are not in the symbol table therefore they have no scope. But this should probably be changed in future to something more rational and faster.
A few minor bugfixes were applied while going through the code.
- Some tabs used as Unicode were written as byte strings. Add the u'\t' prefix.
- After simplifying a%1 -> a&0, fold again the node and return. It's not clear why it didn't return, and whether it depended on subsequent passes (e.g. after DCR) for possibly optimizing out the result. Now we're sure.
- A few places lacked a SEF declaration.
- Formatting changes to split lines that spilled the margin.
- Some comment changes.
- Expanded lazy_list_set definition while adapting it to object format. The plan was to re-compress it after done, but decided to leave it in expanded form.
- Added a few TODOs & FIXMEs, resisting the temptation to fix them in the same commit:
- TODO: ~-~-~-expr -> expr + -3.
- FIXME: Now that we have CompareTrees, we can easily check if expr + -expr cancels out and remove a TODO. Low-hanging fruit.
- TODO: Check what we can do when comparing non-SEF and non-CONST values in '>' (current code relies on converting '>' to '<' for applying more optimizations, but that may miss some opportunities).
- FIXME: Could remove one comparison in nt == '&&' or nt == '||'. Low-hanging fruit.
CleanNode was too greedy, because children of global declarations (particularly lists) are not marked executable. Make a special case for them and don't recurse, since what matters is whether the declaration itself is executed. Its contents can't be cleaned up.
The output module adds parentheses where necessary, depending on the evaluation order in the tree. Or that's the idea. Prone to bugs, let's see how it bodes.