We would use deMorgan as needed to convert:
Compound condition in structured if-then-else statement:
...
if ( i == x && j == y ) {
<then-part>
}
else {
<else-part>
}
...
In if-goto-label form, the condition is negated while also the branching is directed toward the else part, so with both of these changes together, it still runs the same (it is effectively double negation, so same logic):
...
if ( ! (i == x && j == y) ) goto else1Part;
then1Part:
<then-part>
goto endIf1;
else1Part:
<else-part>
endIf1:
...
Negation can be distributed over the conjunction by negating the operands of &&
and changing to ||
.
Application of de Morgan to the negated condition:
if ( ! (i == x) || ! (j == y) ) goto else1Part;
And then optimize the negation of relations:
if ( i != x || j != y ) goto else1Part;
This can the be broken into two if-statements:
if ( i != x ) goto else1Part;
if ( j != y ) goto else1Part;
// will come here when the original if condition is true
And those two lines are easy in assembly.
We can convert &&
to &
as another approach, so rather than implementing the short-circuit operator, we can evaluate both operands and simply and
the results together and test that with single branch instruction. De Morgan can also be applied; while ||
can be replaced with |
.
Converting a short-circuit operator to non-short-circuit equivalent only works if the code allows for it, which means that it needs to be ok for the program to always perform/execute/evaluate the 2nd operand. A function call or an array reference is not necessarily ok to perform, in the case that it is being guarded by the first condition. Here's an example, of when it is not ok to convert short-circuit operator:
if ( i < N && a[i] == 0 ) ...
The array reference is being protected/guarded by a range check using a short-circuit operator, so it would sometimes cause an array reference out of bounds to evaluate both sides of &&
if it were converted to &
.
Function calls in the 2nd operand can also be problematic for this conversion.