Solidity

Solidity Assembly Opcode Stream Generation

Solidity Assembly Opcode Stream Generation Main Tips

  • In Solidity standalone assembly, the process of opcode stream generation is the point where the stack is being modified.
  • Opcode stream generation is also one of the essential stages to standalone assembly happens in.

Solidity Assembly Opcode Stream Generation

As the opcode stream is generated, the current stack height in a counter is being looked after, so stack variable access by name is possible. The stack height is having modification applied to it with every opcode modifying the stack and with each label annotated with an adjustment to the stack made. Every time another local variable gets introduced, it gets registered along with the stack height that currently applies. When a variable is being accessed (for assignment or copying the value), the adequate DUP or SWAP instruction gets selected according to the difference between stack height now and at the moment of introducing the variable.

The following is pseudocode, meaning that it will describe the elements of the script to illustrate how the code should work, but will not compile:

Example

codegen item: AST -> opcode_stream =
match item {
Block({ items }) ->
  join(codegen(item) for item in items)
  if last generated opcode has continuing control flow:
    POP for all local variables registered at the block (including variables
    introduced by labels)
    warning issued in case the stack height here is not similar to 
    the beginning of the block
Identifier(id) ->
  id is looked up in the syntactic block stack
  the type of id is matched
    Local Variable ->
      DUPi where dif = 1 + stackHeight - stackHeightOfIdentifier(id)
    Label ->
      // reference that will be resolved when the bytecode generation happens
      PUSH< label's bytecode position>
    SubAssembly ->
      PUSH<subassembly data's bytecode position>
FunctionalAssemblyExpression(id ( arguments ) ) ->
  join(codegen(arg) for arguments in arguments.reversed())
  id (which must be an opcode, later on may be a function name)
LocalDefinition(let (id1, ..., idn) := expr) ->
  identifiers id1, ..., idn are registered as locals inside current block at the present stack height
  codegen(expr) - it is asserted that expr would return n items to the stack
FunctionalAssignment((id1, ..., idn) := expr) ->
  id1, ..., idn are looked up in the syntactic block stack, it is asserted that they are variables
  codegen(expr)
  for j = n, ..., dif:
  SWAPi where dif = 1 + stackHeight - stackHeightOfIdentifier(idj)
  POP
Assignment(=: id) ->
  id is looked up in the syntactic block stack, it is asserted that it is a variable
  SWAPi where dif = 1 + stackHeight - stackHeightOfIdentifier(id)
  POP
LabelDefinition(name:) ->
  JUMPDEST
NumberLiteral(num) ->
  PUSH<num interpreted as right-aligned and decimal>
LiteralHex(lit) ->
  PUSH32<lit interpreted as left-aligned and hex>
LiteralString(lit) ->
  PUSH32<lit left-aligned and utf-8 encoded>
SubAssembly(assembly <name> block) ->
  codegen(block) is appended at code's end
dataSize(<name>) ->
  it is asserted that <name> is a subassembly ->
  PUSH32<size of code generated from subassembly <name>>
linkerSymbol(<lit>) ->
  PUSH32<zeros> and append position to linker table
}

 

Try on Remix Try live on Hosting

Read previous post:
Solidity Assembly Desugaring

Solidity Assembly Desugaring Main Tips In Solidity standalone assembly, the process of desugaring is meant to remove the switch, for and...

Close