Commit graph

152 commits

Author SHA1 Message Date
Sei Lisa
7fbde0269c Fix indentation typo (cosmetic) 2019-01-04 19:35:36 +01:00
Sei Lisa
42750c56d7 Fix some comments 2019-01-02 00:38:44 +01:00
Sei Lisa
f243f3a3c1 New copyright year 2019-01-01 22:54:34 +01:00
Sei Lisa
76f483fc11 Add scope field to {} nodes
Since we need to add variables, we need to know which scope to add them to. Add this information to the {} node, which is what creates a new scope.

An alternative would be to scan for any variable or label declaration within the braces and use that or create a new one if none, which is more expensive and may waste symbol tables.
2018-12-29 21:10:14 +01:00
Sei Lisa
6f32b4710a Remove handling of corner cases that we decided to not support
There was some remaining code trying to deal with labels in the single statement part of IFs and FORs.
2018-12-26 19:25:11 +01:00
Sei Lisa
b329b2b28e Avoid a possibly unnecessary expansion
When transforming if(!non-bool | !bool) to if(!(non-bool & -bool)), don't negate the bool if non-bool is actually an AND-bool.
2018-12-24 17:22:54 +01:00
Sei Lisa
c9f73bd429 Extend an optimization to cover some (admittedly rare) cases.
The value of bool|const is const when bit 0 of const is 1. This is a generalization of the case bool|1 = 1.
2018-12-24 17:22:54 +01:00
Sei Lisa
141301d7ff Reformatting 2018-12-24 17:22:54 +01:00
Sei Lisa
e123866e6e Apply DeMorgan in some cases that were not caught.
Example: the optimization of if(i < 2 || i) was suboptimal, because FoldTree was done before FoldCond. In a first stage, the || was removed: if(!!(i < 2 | i)); when folded, the inner ! was optimized first and the result was if(!(1 < i & (!i))), which FoldCond wasn't smart enough to handle.

The new optimization takes over from there, and converts if(!(!a & b)) with b boolean, in any order, to if(a | !b). When applied to the above expression, it gets folded to if(i < 2 | i) as expected, which is optimal.

Also, new function: IsAndBool (see docstring), used on b in the above example.
2018-12-24 17:18:48 +01:00
Sei Lisa
0a6155bb13 Apply an associativity rule to strings
It's not general associativity, it just folds CONST + (CONST + expr). Some day we'll implement sum flattening, to make this possible.
2018-12-22 10:27:37 +01:00
Sei Lisa
b9fe0b6c85 Add TODO item about side-effect analysis of UDFs 2018-11-19 21:15:52 +01:00
Sei Lisa
18d19b4653 Fix bug where 1 + (2 + function) was folded as 4 + function
After folding, our cached values changed under us. Re-cache them.
2018-11-19 20:43:58 +01:00
Sei Lisa
960cbfaa09 Fix "Value of unknown type" on bit shift optimization
When converting x << constant to a product, we neglected to truncate the converted constant to S32.

Per bug report by Tonaie Resident.
2018-07-25 15:59:04 +02:00
Sei Lisa
6502a1a0b6 Remove TODO because there was nothing to do after checking. 2018-06-08 00:14:16 +02:00
Sei Lisa
e88e39127b Save a comparison (fixes a FIXME) 2018-06-08 00:07:36 +02:00
Sei Lisa
0808938894 Optimizations based on addition algebra for floats and integers
Float addition is commutative. Swap the constant to the left side if it's not there. This is a "cheap" version of a bigger change that is planned, to minimize stack usage as much as possible on savepoints.

Float addition is not associative, therefore we don't optimize e.g. 1 + (2 + float).

Integer addition is commutative and associative. If there's a constant, we swap it to the left side. If there's a chained summation of the form const + (const + expression), we apply associativity to turn it into (const + const) + expression and reoptimize.

This doesn't cover all possible cases. Expressions of the form (const + expr) + (const + expr) are not optimized. We need to flatten sums if we want to do the right thing here, but that's not yet implemented.

Get rid of some older code and TODOs that are no longer needed.
2018-06-07 23:57:21 +02:00
Sei Lisa
ac254efb77 Minor details
Assert that it can't be key where we say it can't, and that it is integer where we say it must.

Adjust some comments.
2018-06-07 02:14:15 +02:00
Sei Lisa
a33dc8540f Implement ~-~-~-expr -> expr + -3
Gets rid of a TODO
2018-06-07 02:14:15 +02:00
Sei Lisa
89c627b0c0 Remove jumps that just go to the next statement
This might help a bit with switch().
2018-05-17 09:46:06 +02:00
Sei Lisa
285c7172fd Get rid of DoesSomething() in favour of SEF
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.
2018-05-17 09:08:48 +02:00
Sei Lisa
a93ea0ca19 Delete statements in a {} block when they are SEF
This is probably equivalent to what we did, as FoldStmt changes SEF statements to ';' statements, but it's simpler.
2018-05-17 09:08:35 +02:00
Sei Lisa
caf50c4e14 Change while (cond) /*empty*/; into do /*empty*/; while (cond); 2018-05-17 06:38:33 +02:00
Sei Lisa
8f93386108 Get rid of label checks in some situations no longer supported 2018-05-17 06:38:12 +02:00
Sei Lisa
6ef4c03994 Remove support for labels as immediate children of IF/ELSE/WHILE/FOR/DO.
This extremely uncommon coding pattern was becoming a hell to support. It has caused many bugs in past that need them being treated as special cases.

Getting rid of the possibility entirely seems like the best approach.

It's still supported if the code is not to be optimized (e.g. with --pretty).
2018-04-01 20:05:35 +02:00
Sei Lisa
075d3aba0c Change the AST node type from dict to object
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.
2018-04-01 02:14:00 +02:00
Sei Lisa
877d5fc10c Optimize -a == -b, !(a - b), !(a + b)
-a == -b  ->  a == b

If both a and b either are constants or have a minus sign, negate both.

!(a - b) can be optimized to a == b.

!(a + b) can be optimized to -a == b, relying on the first optimization to remove redundant minus signs.
2018-03-23 19:02:42 +01:00
Sei Lisa
d0d6d6744b Bump copyright year
The help text in main.py was still at 2015. Oops.
2018-03-23 16:36:45 +01:00
Sei Lisa
a1f1f13739 Better optimization of (non-)equality comparisons.
int != int was not properly optimized, because the != was transformed into the equivalent !(int == int) at an earlier stage. Fixed.

!(a ^ b) can be optimized to a == b, so do it.
2018-03-23 16:04:18 +01:00
Sei Lisa
e42479756b Fix bug with nested if's; fix missing EXPR wrap.
097c054 introduced a bug that we hadn't caught until now.

In some occasions, it could swap nested conditions in such a way that the 'else' of the outer statement was made to belong to the inner one, like this:

    if (a)
        if (b)
            stuff;
    else
        stuff;

That is of course parsed with the 'else' belonging to if(b).

Fix implemented at output time, by detecting 'if(a) stmt; else y;' with stmt being an 'if' without 'else', and wrapping the stmt in {} like this: 'if(a){if(b) x;} else y;'. This has some similarity with parenthesis addition.

But the fix has the corner case that, since {} hides visibility of labels, when the inner 'if' has a label as direct child, it can't be swapped lest the label becomes out of scope. So these cases are detected and skipped in the constant folding module.

In the case of 'if(cond);', we transform it to 'cond;', but we forgot to wrap the cond in an EXPR node as required. Fixed too.
2017-11-13 04:04:13 +01:00
Sei Lisa
a4b3c1eadd Handle list+list more sanely. 2017-11-04 22:50:32 +01:00
Sei Lisa
0b1ad7c110 Add more results of operating an expression with itself.
Implemented a<a=0, a^a=0, a+-a=0, -a+a=0, a|a=a, a&a=a
2017-11-03 00:45:11 +01:00
Sei Lisa
4251d4c7f9 Improve CompareTrees.
Reorganize into different statements with early return.

Add constants, unary operators and binary operators. Check if operator is commutative and check with operands swapped when so.

Constant equality is somewhat sketchy at the moment: just compare the values with Python's ==.
2017-11-03 00:12:10 +01:00
Sei Lisa
a87022b73f Get rid of StSw craziness and use lsllastpass to make another pass.
This has been a TODO item for long. Now that we have lsllastpass, it's actually easy to implement.

Adds an LSLTypeDefaults dictionary to lslcommon, just in case the state-changing function returns a value and we need to insert a return statement.

We've also added subtree-local info to lsllastpass (lost when we return to the parent after visiting a subtree).

This fixes a bug where naked switch statements could appear as a result of optimization, and cause the compilation to fail.
2017-11-02 18:20:14 +01:00
Sei Lisa
e4eaab9e84 Move the print_node() subtree dump debug function to lslcommon. 2017-11-02 16:34:35 +01:00
Sei Lisa
097c054494 Optimize 'if' statements better.
if (!cond) X; else Y;  ->  if (cond) Y; else X;

if (int1 == int2) X; else Y;  ->  if (int1 ^ int2) Y; else X;

When 'cond' is of a type other than 'key': if (cond) ; else X;  ->  if (!cond) X;  (this required changing if(str) to its compiled equivalent if(!(str == "")), so that 'cond' is always either key or integer).

if (cond) ;  ->  cond; and folds it as a statement, which may eliminate it if it's SEF. This is done after eliminating 'else ;' so that it also optimizes 'if (cond) ; else ;' the same way.

This removes a TODO item.
2017-10-30 19:23:01 +01:00
Sei Lisa
ed49170d87 Remove empty SEF events.
If the state becomes empty, add an empty timer() event. timer() is the parameter-less event with the shortest name.
2017-10-30 18:05:35 +01:00
Sei Lisa
e13f210695 Make DoesSomething apply to labels optionally.
Allows detection of empty events, for example, even if they have labels.

Also, it is OK if there's a label inserted in a nested {}; that case wasn't contemplated.
2017-10-30 18:05:28 +01:00
Sei Lisa
70f39d7f5e Remove "magic names" where possible, using new data from fndata.txt 2017-10-27 23:39:26 +02:00
Sei Lisa
3bb5102f06 Improve llStringLength detection, to catch more cases.
For example, if (llStringLength(s) > 0)  ->  if (!(s == ""))
2017-10-27 16:07:36 +02:00
Sei Lisa
1dde2a1fb9 Use lslfuncs.less instead of > >= < <= to ensure proper type handling.
Float functions could be compared against integer args, or vice versa.
2017-10-27 11:20:42 +02:00
Sei Lisa
c0176ad738 Fix order change in comparisons when one side is not SEF.
The comment was wrong anyway. If one side changes x and the other side uses x, then order is still important, no matter whether one side is SEF.

But the reversal is safe when one side is a constant, so we still perform it, to enable optimization of some important cases.
2017-10-27 11:13:03 +02:00
Sei Lisa
106bb81543 Refine a comment.
Powers of two can't be zero anyway.
2017-10-26 01:07:48 +02:00
Sei Lisa
4ba71993a4 Optimize min/max in comparisons; undo problematic optimization.
For floats:

When const >= function.max, comparisons of function > const always yield FALSE.

When const <  function.min, comparisons of function > const always yield TRUE.

When const >  function.max, comparisons of function < const always yield TRUE.

When const <= function.min, comparisons of function < const always yield FALSE.

For integers:

When min = -1, cond(function > -1) is the same as cond(!~function).

When min = -1, cond(function < 0) is the same as cond(~function).

To implement the above, we got rid of the cond(x < 0)  ->  cond(x & 0x80000000) optimization, which has caused more trouble than it has solved for just 1 byte gain.

When min = 0, cond(function > 0) is the same as cond(function).

When min = 0, cond(function < 1) is the same as cond(!function).

Similar expressions can be obtained for max in [-1, 0], but it's not worth it, as there are no functions with -1 as maximum, and the ones with max=0 also have min=0 (always return 0).
2017-10-26 00:58:14 +02:00
Sei Lisa
22e057e2ec Move side-effect-free check for functions to a method.
This will in future allow us to check argument-dependent SEFness.
2017-10-25 19:24:12 +02:00
Sei Lisa
2580248c17 Minor reorganization and documentation of a section. 2017-10-25 18:04:27 +02:00
Sei Lisa
7e39635b09 Transform !(x != y) into x == y.
Can happen with lists.
2017-10-21 20:38:54 +02:00
Sei Lisa
923309e4a1 Refine test for min and max.
We had dormant code to check for boolean-ness of functions, which is now active. But it didn't cover all possible booleans. Now it does.

An idea for the future is to associate ranges to expressions, and attach them to calculable functions. For example, (integer)llFrand(2) could be resolved to a boolean.
2017-10-21 11:31:53 +02:00
Sei Lisa
ea9711642c Add type casting comparison to CompareTrees.
It can now resolve e.g. (string)llGetPermissions() == (string)llGetPermissions() to TRUE.
2017-10-21 11:03:43 +02:00
Sei Lisa
dc117c2cbf Use CompareTrees to optimize the == operator.
Now we can.
2017-10-21 11:02:38 +02:00
Sei Lisa
e6b23a2d7a Fix CompareTrees to not compare unstable functions as equal. 2017-10-21 10:44:09 +02:00