Code separation

On both EVM and EraVM the code is separated into two parts: deploy code and runtime code. The deploy code is executed only once, when the contract is deployed. The runtime code is executed every time the contract is called. However, on EraVM the deploy code and runtime code are deployed together, and they are not split into two separate chunks of bytecode.

The constructor is added to the contract as a regular public function which is called by System Contracts during deployment.

Just like on EVM, the deploy code on EraVM is represented by a single constructor, named differently in different languages:

LanguageName
Solidityconstructor
Yulobject "<name>"
Vyper__init__

The constructor is merged with the runtime code by the LLVM IR generator of our compiler, and a minimal contract on EraVM looks like on the examples below.

LLVM IR

In the example below, contract @__entry arguments %0-%11 correspond to registers r1-r12 on EraVM.

; Function Attrs: nofree noreturn null_pointer_is_valid
define i256 @__entry(ptr addrspace(3) nocapture readnone %0, i256 %1, i256 %2, i256 %3, i256 %4, i256 %5, i256 %6, i256 %7, i256 %8, i256 %9, i256 %10, i256 %11) local_unnamed_addr #1 personality ptr @__personality {
entry:
  %is_deploy_code_call_flag_truncated = and i256 %1, 1                                                          ; check if the call is a deploy code call
  %is_deploy_code_call_flag.not = icmp eq i256 %is_deploy_code_call_flag_truncated, 0                           ; invert the flag
  br i1 %is_deploy_code_call_flag.not, label %runtime_code_call_block, label %deploy_code_call_block            ; branch to the deploy code block if the flag is set

deploy_code_call_block:                           ; preds = %entry
  store i256 32, ptr addrspace(2) inttoptr (i256 256 to ptr addrspace(2)), align 256                            ; store the offset of the array of immutables
  store i256 0, ptr addrspace(2) inttoptr (i256 288 to ptr addrspace(2)), align 32                              ; store the length of the array of immutables
  tail call void @llvm.syncvm.return(i256 53919893334301279589334030174039261352344891250716429051063678533632) ; return the array of immutables using EraVM return ABI data encoding
  unreachable

runtime_code_call_block:                          ; preds = %entry
  store i256 42, ptr addrspace(1) null, align 4294967296                                                        ; store a value to return
  tail call void @llvm.syncvm.return(i256 2535301200456458802993406410752)                                      ; return the value using EraVM return ABI data encoding
  unreachable
}

EraVM Assembly

        .text
        .file   "default.yul"
        .globl  __entry
__entry:
.func_begin0:
        and!    1, r2, r1                           ; check if the call is a deploy code call
        jump.ne @.BB0_1                             ; branch to the deploy code block if the flag is set
        add     42, r0, r1                          ; move the value to return into r1
        st.1    0, r1                               ; store the value to return
        add     @CPI0_1[0], r0, r1                  ; move the return ABI data into r1
        ret.ok.to_label r1, @DEFAULT_FAR_RETURN     ; return the value
.BB0_1:
        add     32, r0, r1                          ; move the offset of the array of immutables into r1
        st.2    256, r1                             ; store the offset of the array of immutables
        st.2    288, r0                             ; store the length of the array of immutables
        add     @CPI0_0[0], r0, r1                  ; move the return ABI data into r1
        ret.ok.to_label r1, @DEFAULT_FAR_RETURN     ; return the array of immutables
.func_end0:

        .note.GNU-stack
        .rodata
CPI0_0:
        .cell 53919893334301279589334030174039261352344891250716429051063678533632
CPI0_1:
        .cell 2535301200456458802993406410752

Made with ❤️ by the ZKsync Community