Inline Assembly

Opcodes

<opcode> ::=
    | "stop" | "add" | "mul" | "sub" | "div" | "sdiv" | "mod" | "smod" | "addmod" | "mulmod" | "exp"
    | "signextend" | "lt" | "gt" | "slt" | "sgt" | "eq" | "iszero" | "and" | "or" | "xor" | "not"
    | "byte" | "shl" | "shr" | "sar" | "sha3" | "address" | "balance" | "origin" | "caller"
    | "callvalue" | "calldataload" | "calldatasize" | "calldatacopy" | "codesize" | "codecopy"
    | "gasprice" | "extcodesize" | "extcodecopy" | "returndatasize" | "returndatacopy"
    | "extcodehash" | "blockhash" | "coinbase" | "timestamp" | "number" | "prevrandao" | "gaslimit"
    | "chainid" | "selfbalance" | "basefee" | "pop" | "mload" | "mstore" | "mstore8" | "sload"
    | "sstore" | "jump" | "jumpi" | "pc" | "msize" | "gas" | "jumpdest" | "push0" | "dup1" | "dup2"
    | "dup3" | "dup4" | "dup5" | "dup6" | "dup7" | "dup8" | "dup9" | "dup10" | "dup11" | "dup12"
    | "dup13" | "dup14" | "dup15" | "dup16" | "swap1" | "swap2" | "swap3" | "swap4" | "swap5"
    | "swap6" | "swap7" | "swap8" | "swap9" | "swap10" | "swap11" | "swap12" | "swap13" | "swap14"
    | "swap15" | "swap16" | "log0" | "log1" | "log2" | "log3" | "log4" | "create" | "call"
    | "callcode" | "return" | "delegatecall" | "create2" | "staticcall" | "revert" | "invalid"
    | "selfdestruct" | <numeric_literal> | <ident> ;

Dependencies:

The <opcode> is one of the mnemonic EVM instructions, or a numeric literal, or an identifier.

Inline Assembly Block

<assembly_output> ::= <ident> | "_" ;

<inline_assembly> ::=
    "asm"
    "(" [<expr> ("," <expr>)* [","]] ")"
    "->" "(" [<assembly_output> ("," <assembly_output>)* [","]] ")"
    "{" (<opcode>)* "}"

Dependencies:

The <inline_assembly> consists of the "asm" keyword, followed by an optional comma separated, parenthesis delimited list of argument expressions, then an arrow, an optional comma separated, parenthesis delimited list of return identifiers, and finally a code block containing only the <opcodes>.

Semantics

Arguments are ordered such that the state of the stack at the start of the block, top to bottom, is the list of arguments, left to right. Identifiers in the output list are ordered such that the state of the stack at the end of the assembly block, top to bottom, is the list of outputs, left to right.

Note that if the input arguments contain local variables, the stack scheduling required to construct the pre-assembly stack state may be unprofitable in cases with small assembly code blocks.

asm (1, 2, 3) -> (a) {
    // state:   // [1, 2, 3]
    add         // [3, 3]
    mul         // [9]
}

Inside the assembly block, numeric literals are implicitly converted into pushN instructions. All literals are put into the smallest N for pushN by bits, however, this is also accounting for leading zeros. For example, 0x0000 would become push2 0000 to allow for bytecode padding. Identifiers may be variables, constants, or ad hoc opcodes. When identifiers are variables, they are scheduled in the stack. When identifiers are constants, they are replaced with their push instructions just as numeric literals are. When identifiers are ad hoc opcodes, they are replaced with their respective byte(s).