spillerrec wrote:
Since a && expression becomes false when just one part is false, we can use this together with short-circuiting to just enter the else clause when just one part evaluates to false. There is no need to remember the results of the previous parts.
This looks like a completely safe suggestion and I have implemented this change to BoolSubExpression (which handles BoolTerm || BoolTerm) and to BoolTerm (which handles BitOr && BitOr). Both of these routines used to push the primary register onto the stack after the lhs and then after the rhs they would pop the primary register off the stack and OR or AND it with the value of the rhs. I already had jumps in place for short-circuit evaluation so all I had to do was comment out PushPrim and PopOr|PopAnd in these two routines.
If it is an expression like "if( b1 && x+y )" x+y should be evaluated just before the comparison
This is where your suggestion falls apart. The problem is that you cannot ignore the value of the boolean expression. Its value is not x+y. Its value is 0 or 1. In a simple case like you have shown it does happen to be the case that the expression (or sub-expression) value is discarded so it does not need to be stored in the primary register. But a boolean expression can be the rhs of an assignment like this:
The parser code that processes the rhs of this assignment is exactly the same code that processes an if-condition. So if I want the value discarded (or never stored) in the case of an if-condition then I would need to set some kind of flag before I parse the if-condition that says 'don't bother storing the value in this situation'. But even then it is not safe to assume that the value of a boolean expression never needs to be stored just because the expression is part of an if-condition. Like this:
If I do not store the value of the boolean expression in the primary register then the resulting code is wrong:
Code: Select all
subcall __initialize_global_data, ____initialize_global_data_return
mov __D0main, __main_7qG2_b1_7qG2_000
brtst 4, __ASM_Label_609, __D0main
mov __D0main, __main_7qG2_b2_7qG2_000
brtst 4, __ASM_Label_609, __D0main
add __D0main, __main_7qG2_x_7qG2_000, __main_7qG2_y_7qG2_000
__ASM_Label_609:
mov __main_7qG2_y_7qG2_000, __D0main ; this stores x+y into y (WRONG)
mov __main_7qG2_x_7qG2_000, __D0main ; this stores x+y into y (WRONG)
brtst 4, __ASM_Label_610, __D0main
wait 1000
__ASM_Label_610:
And if your simple variables had a value other than zero or 1 they would also put the wrong value into x and y. Even a boolean variable could have any non-zero value stored in it as TRUE (i.e., 42) but its value in a boolean expression is simply 0 or 1.
And it is not only assignments inside an if-condition that throw a serious wrench into your suggestion. You can compare the value of a boolean expression in >,<,==,!=, <=, and >= comparison expressions. You can bitwise AND or OR the value of a boolean expression into another value. You can add, multiply, subtract, or divide the value of a boolean expression with another value or other boolean expression. Any of these operations will fail if you do not always store the value of each boolean expression into the primary register - in case it is needed subsequently. I can't guess that because the boolean expression happens to be within an if-condition or while-condition that it is safe to discard these values.
Interestingly, every single test program that I have appeared to run correctly yesterday when I was testing the compiler with storing the value of the boolean expression turned off. While the compiler was in this state it was producing code like you see above (which is very much like what you suggested, and with optimization of mov|brtst sequences would be essentially identical). So I can see why it is so enticing to want this kind of simplicity in the code that the compiler generates. And in many typical if-conditions or other uses of boolean expressions it works perfectly fine. It just is not right all the time and figuring out on the front end is nearly impossible.
So maybe I just need to have a really smart optimizer on the back end that can get rid of all the extra code. The problem there is that it is as hard, if not harder, on the back end to be certain that nobody needs the value that you put into the primary register variable. Once you branch or have labels in between two lines of code then pretty much all bets are off. Just look at the NBC code above. I can't know for certain that the 0 or 1 value that I put in the primary register before the first branch will be needed when I store the value of the primary register in X and Y further down.
Anyway, it was a good exercise to go through how the NXC front end handles boolean expressions and thanks to this prodding I was able to come to the realization that I did not need to PushPrim and PopOr|PopAnd like I was doing. This saves a couple lines of code.
John Hansen