Skip to content

Optimize DUP1, ISZERO and POP in ifStatement #15097

Open
@molly-ting

Description

@molly-ting

Description

   function and(uint256 a, uint256 b) public returns(uint256) {
        if (a > b || a > 100) {
            return a - b;
        } else {
            return a+b;
        }
    }

The above function will generate the following opcode.

PUSH1 0x0 DUP2 DUP4 GT DUP1 PUSH2 0x70 JUMPI POP PUSH1 0x64 DUP4 GT JUMPDEST ISZERO PUSH2 0x88 JUMPI DUP2 DUP4

The corresponding assembly code is:

...
tag_7:
        /* "condition.sol":270:277  uint256 */
      0x00
        /* "condition.sol":297:298  b */
      dup2
        /* "condition.sol":293:294  a */
      dup4
        /* "condition.sol":293:298  a > b */
      gt
        /* "condition.sol":293:309  a > b || a > 100 */
      dup1
      tag_11
      jumpi
      pop
        /* "condition.sol":306:309  100 */
      0x64
        /* "condition.sol":302:303  a */
      dup4
        /* "condition.sol":302:309  a > 100 */
      gt
        /* "condition.sol":293:309  a > b || a > 100 */
tag_11:
        /* "condition.sol":289:348  if (a > b || a > 100) {... */
      iszero
      tag_12
      jumpi
        /* "condition.sol":336:337  b */
      dup2
        /* "condition.sol":332:333  a */
      dup4
        /* "condition.sol":332:337  a - b */
…

In tag_7, there is a 'POP' just after 'JUMPI', which means the original value of the DUP is not used. Although this value is used in the other branch(tag_11), this can also be optimized.

Optimization

Specifically, moving tag_11 after iszero tag_12 jumpi would achieve this optimization. The resulting assembly code is as follows:

...
tag_7:
        /* "condition.sol":270:277  uint256 */
      0x00
        /* "condition.sol":297:298  b */
      dup2
        /* "condition.sol":293:294  a */
      dup4
        /* "condition.sol":293:298  a > b */
      gt
        /* "condition.sol":293:309  a > b || a > 100 */
      tag_11
      jumpi
        /* "condition.sol":306:309  100 */
      0x64
        /* "condition.sol":302:303  a */
      dup4
        /* "condition.sol":302:309  a > 100 */
      gt
        /* "condition.sol":293:309  a > b || a > 100 */
      iszero
      tag_12
      jumpi
tag_11:
        /* "condition.sol":336:337  b */
      dup2
        /* "condition.sol":332:333  a */
      dup4
        /* "condition.sol":332:337  a - b */
…

In this way, for a>b, it will execute PUSH1 DUP2 DUP4 GT PUSH2 JUMPI JUMPDEST DUP2 DUP4 instead of PUSH1 DUP2 DUP4 GT DUP1 PUSH2 JUMPI JUMPDEST ISZERO PUSH2 JUMPI DUP2 DUP4 . For a<=b and a>100, it will execute PUSH1 DUP2 DUP4 GT PUSH2 JUMPI PUSH1 DUP4 GT ISZERO PUSH2 JUMPI JUMPDEST DUP2 DUP4 instead of PUSH1 DUP2 DUP4 GT DUP1 PUSH2 JUMPI POP PUSH1 DUP4 GT JUMPDEST ISZERO PUSH2 JUMPI DUP2 DUP4 .

void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
{
Token const c_op = _binaryOperation.getOperator();
solAssert(c_op == Token::Or || c_op == Token::And, "");
_binaryOperation.leftExpression().accept(*this);
m_context << Instruction::DUP1;
if (c_op == Token::And)
m_context << Instruction::ISZERO;
evmasm::AssemblyItem endLabel = m_context.appendConditionalJump();
m_context << Instruction::POP;
_binaryOperation.rightExpression().accept(*this);
m_context << endLabel;
}

In this function, the endLabel is tag_11 in this example.
bool ContractCompiler::visit(IfStatement const& _ifStatement)
{
StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
compileExpression(_ifStatement.condition());
m_context << Instruction::ISZERO;
evmasm::AssemblyItem falseTag = m_context.appendConditionalJump();
evmasm::AssemblyItem endTag = falseTag;
_ifStatement.trueStatement().accept(*this);
if (_ifStatement.falseStatement())
{
endTag = m_context.appendJumpToNew();
m_context << falseTag;
_ifStatement.falseStatement()->accept(*this);
}
m_context << endTag;
checker.check();
return false;
}

Is it possible to move the Instruction::ISZERO and conditionalJump in this function to the front of the endLabel in the function appendAndOrOperatorCode?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions