Bytecode patterns for && and ||
Think about what a short-circuiting operator like && or || actually does. You've got some conditional branching. Let's consider &&. What you're effectively evaluating is:
if (left)
if (right) <do something>
endIf
There is no single bytecode instruction that can describe this behavior. You need need some labels and conditional branching instructions:
.start
<left expression>
IFEQ .endIf // if left evaluates to zero (false), skip to end
<right expression>
IFEQ .endIf // if right evaluates to zero (false), skip to end
.ifTrue
<body of 'if' block>
.endIf
The behavior of the || operator is a bit different; in this case, the logic looks something like this:
if (left)
goto .ifTrue
if (!right)
goto .endIf
.ifTrue
<do something>
.endIf
Note how the check on the right operand is inverted to avoid an additional branch when the right operand evaluates to true. This behavior could be implemented in bytecode like so:
<left operand>
IFNE .ifTrue // if left evaluates true, skip right, enter 'if' body
<right operand>
IFEQ .endIf // if right evaluates false, skip 'if' body
.ifTrue
<do something>
.endIf
When to push your operands
Note that your original question suggested you already have the left and right operands on the stack; that would be bad. You should only evaluate the right operand after the left operand has evaluated to true (nonzero) for && or false (zero) for ||. If the right operand causes side effects, evaluating it prematurely would violate the defined behavior of these operators.