Why does Java not optimize |= assignments?

195 Views Asked by At

t*() always returns true for this example, where as f*() always returns false.

Let's say we have the following expression

if ( f1() || t1() || f2() || t2() ){
    // do stuff        
}

If this is the case the JVM optimizes the execution and only executes f1() and t1() because it 'understands' that no matter what f2() and t2() yield, the requirement for entering the if-statement is fulfilled and therefor no further calculations are required.

I'm working on a code where I wrote something like this:

boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D

One of my colleagues saw this and mentioned that he is not sure, but it could be possible that Java optimizes the statements C and D, because b is always true from statement B onward and this could lead to some issues.

I conducted some tests, it seems as if all of them are properly executed (this is the desired behavior), but I'm still wondering why doesn't this get optimized? I figured he might be right and the JVM understands that once b is true no |= operation on it will change its value.

3

There are 3 best solutions below

0
On BEST ANSWER

I'm still wondering why doesn't this get optimized?

Because that would be a violation of the JLS.

The statement

b |= f1();

is equivalent to

b = (boolean)(b | f1());

In the above, the JLS requires that b | f1() is evaluated as follows:

  1. Get the value of b.
  2. Call f1() and capture the resulting value
  3. Apply the | operator to the two values.

The JLS does not allow the compiler to skip the call f1() if b is true1.

If you want that semantic (short-circuiting), you need to use b = b || f1(); and so on. (As you noted: b ||= f1() is a syntax error.)


1 - Actually, in a situation where it was not possible to observe (in a single-threaded program) that an f1() call had or had not happened, the optimization would in theory be permissible. But you would only to be able to detect the optimization by carefully examining the native code emitted by the JIT compiler. It it could only happen if the call was strictly side-effect free.

2
On

This is more about the difference between boolean and bitwise operators.

The |= compound operator is a bitwise operator, meaning that both terms are evaluated.

You can easily debug this by setting a test where b is assigned literal true, followed by a |= assignment to a method that returns either boolean value, and has a breakpoint in it.

The breakpoint will always trigger.

The "shortcut" optimization on the other hand is available only for boolean operators: || and &&.

Note: some specs on compound assignment here, but I couldn't find a relevant part on the |= assignment.

0
On

The calls don't get optimized away because JLS §15.26.2. Compound Assignment Operators requires the right-hand side expression to be evaluated.

If the left-hand operand expression is not an array access expression, then:

  • First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
  • Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated.

  • ...

Historically, the tradition of short-circuiting conditional (&&, ||) but not bitwise (&, |) operators goes back to at least C (but it might be worth noting that C didn't have an explicit boolean type until 1999).