ewasm / evm2wasm

[ORPHANED] Transcompiles EVM code to eWASM
Mozilla Public License 2.0
107 stars 38 forks source link

[CI] Ensure Binaryen validates wast artifacts #275

Open jwasinger opened 6 years ago

jwasinger commented 6 years ago

Currently, the wast for CALL ( https://github.com/ewasm/evm2wasm/blob/master/wasm/wast.json#L91 ) fails to load. We should add linting to CircleCI to catch changes that break Binaryen.

jwasinger commented 6 years ago

Here is the wast segment that is generated for callcall_00:

(module
  (import "ethereum" "storageStore" (func $storageStore (param i32 i32) ))

(import "ethereum" "call" (func $call (param i64 i32 i32 i32 i32) (result i32)))

(import "ethereum" "useGas" (func $useGas (param i64)))
  (global $cb_dest (mut i32) (i32.const 0))
  (global $sp (mut i32) (i32.const -32))
  (global $init (mut i32) (i32.const 0))

  ;; memory related global
  (global $memstart i32  (i32.const 33832))
  ;; the number of 256 words stored in memory
  (global $wordCount (mut i64) (i64.const 0))
  ;; what was charged for the last memory allocation
  (global $prevMemCost (mut i64) (i64.const 0))

  ;; TODO: memory should only be 1, but can't resize right now
  (memory 500)
  (export "memory" (memory 0))

  ;; generated by ./wasm/generateInterface.js
(func $SSTORE   (call $storageStore(get_global $sp)(i32.add (get_global $sp) (i32.const -32))))(func $PUSH
  (param $a0 i64)
  (param $a1 i64)
  (param $a2 i64)
  (param $a3 i64)
  (local $sp i32)

  ;; increament stack pointer
  (set_local $sp (i32.add (get_global $sp) (i32.const 32)))

  (i64.store (get_local $sp) (get_local $a3))
  (i64.store (i32.add (get_local $sp) (i32.const 8)) (get_local $a2))
  (i64.store (i32.add (get_local $sp) (i32.const 16)) (get_local $a1))
  (i64.store (i32.add (get_local $sp) (i32.const 24)) (get_local $a0))
)
;; generated by ./wasm/generateInterface.js
(func $CALL (local $offset0 i32)(local $length0 i32) (set_local $offset0 
    (call $check_overflow
      (i64.load (i32.add (get_global $sp) (i32.const -96)))
      (i64.load (i32.add (get_global $sp) (i32.const -88)))
      (i64.load (i32.add (get_global $sp) (i32.const -80)))
      (i64.load (i32.add (get_global $sp) (i32.const -72)))))(set_local $length0 
    (call $check_overflow 
      (i64.load (i32.add (get_global $sp) (i32.const -128)))
      (i64.load (i32.add (get_global $sp) (i32.const -120)))
      (i64.load (i32.add (get_global $sp) (i32.const -112)))
      (i64.load (i32.add (get_global $sp) (i32.const -104)))))

    (call $memusegas (get_local $offset0) (get_local $length0))
    (set_local $offset0 (i32.add (get_global $memstart) (get_local $offset0))) (i64.store
      (i32.add (get_global $sp) (i32.const -192))
      (i64.extend_u/i32
        (i32.eqz (call $call(call $check_overflow_i64
           (i64.load (i32.add (get_global $sp) (i32.const 0)))
           (i64.load (i32.add (get_global $sp) (i32.const 8)))
           (i64.load (i32.add (get_global $sp) (i32.const 16)))
           (i64.load (i32.add (get_global $sp) (i32.const 24))))
          (i32.add (get_global $sp) (i32.const -32))(i32.add (get_global $sp) (i32.const -64))(get_local $offset0)(get_local $length0)
      ;; zero out mem
      (i64.store (i32.add (get_global $sp) (i32.const -128)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -136)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -144)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -152)) (i64.const 0))
      ;; zero out mem
      (i64.store (i32.add (get_global $sp) (i32.const -160)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -168)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -176)) (i64.const 0))
      (i64.store (i32.add (get_global $sp) (i32.const -184)) (i64.const 0))) ;; flip CALL result from EEI to EVM convention (0 -> 1, 1,2,.. -> 1)
      )))
    ;; zero out mem
    (i64.store (i32.add (get_global $sp) (i32.const -168)) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const -176)) (i64.const 0))
    (i64.store (i32.add (get_global $sp) (i32.const -184)) (i64.const 0)))(func $bswap_i64
  (param $int i64)
  (result i64)

  (i64.or
    (i64.or
      (i64.or
        (i64.and (i64.shr_u (get_local $int) (i64.const 56)) (i64.const 0xff)) ;; 7 -> 0
        (i64.and (i64.shr_u (get_local $int) (i64.const 40)) (i64.const 0xff00))) ;; 6 -> 1
      (i64.or
        (i64.and (i64.shr_u (get_local $int) (i64.const 24)) (i64.const 0xff0000)) ;; 5 -> 2
        (i64.and (i64.shr_u (get_local $int) (i64.const  8)) (i64.const 0xff000000)))) ;; 4 -> 3
    (i64.or
      (i64.or
        (i64.and (i64.shl (get_local $int) (i64.const 8))   (i64.const 0xff00000000)) ;; 3 -> 4
        (i64.and (i64.shl (get_local $int) (i64.const 24))   (i64.const 0xff0000000000))) ;; 2 -> 5
      (i64.or
        (i64.and (i64.shl (get_local $int) (i64.const 40))   (i64.const 0xff000000000000)) ;; 1 -> 6
        (i64.and (i64.shl (get_local $int) (i64.const 56))   (i64.const 0xff00000000000000))))) ;; 0 -> 7
)
(func $bswap_m256
  (param $sp i32)
  (result i32)
  (local $temp i64)

  (set_local $temp (call $bswap_i64 (i64.load (get_local $sp))))
  (i64.store (get_local $sp) (call $bswap_i64 (i64.load (i32.add (get_local $sp) (i32.const 24)))))
  (i64.store (i32.add (get_local $sp) (i32.const 24)) (get_local $temp))

  (set_local $temp (call $bswap_i64 (i64.load (i32.add (get_local $sp) (i32.const 8)))))
  (i64.store (i32.add (get_local $sp) (i32.const  8)) (call $bswap_i64 (i64.load (i32.add (get_local $sp) (i32.const 16)))))
  (i64.store (i32.add (get_local $sp) (i32.const 16)) (get_local $temp))
  (get_local $sp)
)
(func $callback
  (call $main)
)
(func $callback_32
  (param $result i32)

  (i64.store (get_global $sp) (i64.extend_u/i32 (get_local $result)))
  ;; zero out mem
  (i64.store (i32.add (get_global $sp) (i32.const 24)) (i64.const 0))
  (i64.store (i32.add (get_global $sp) (i32.const 16)) (i64.const 0))
  (i64.store (i32.add (get_global $sp) (i32.const 8)) (i64.const 0))

  (call $main)
)
(func $check_overflow
  (param $a i64)
  (param $b i64)
  (param $c i64)
  (param $d i64)
  (result i32)

  (local $MAX_INT i32)
  (set_local $MAX_INT (i32.const -1))

  (if
    (i32.and 
      (i32.and 
        (i64.eqz  (get_local $d))
        (i64.eqz  (get_local $c)))
      (i32.and 
        (i64.eqz  (get_local $b))
        (i64.lt_u (get_local $a) (i64.extend_u/i32 (get_local $MAX_INT)))))
     (return (i32.wrap/i64 (get_local $a))))

     (return (get_local $MAX_INT))
)
(func $check_overflow_i64
  (param $a i64)
  (param $b i64)
  (param $c i64)
  (param $d i64)
  (result i64)

  (if
    (i32.and 
      (i32.and 
        (i64.eqz  (get_local $d))
        (i64.eqz  (get_local $c)))
      (i64.eqz  (get_local $b)))
    (return (get_local $a)))

    (return (i64.const 0xffffffffffffffff))
)
;;
;; memcpy from ewasm-libc/ewasm-cleanup
;;
(func $memset
  (param $ptr i32)
  (param $value i32)
  (param $length i32)
  (result i32)
  (local $i i32)

  (set_local $i (i32.const 0))

  (block $done
    (loop $loop
      (if (i32.ge_u (get_local $i) (get_local $length))
        (br $done)
      )

      (i32.store8 (i32.add (get_local $ptr) (get_local $i)) (get_local $value))

      (set_local $i (i32.add (get_local $i) (i32.const 1)))
      (br $loop)
    )
  )
  (get_local $ptr)
)
(func $memusegas
  (param $offset i32)
  (param $length i32)

  (local $cost i64)
  ;; the number of new words being allocated
  (local $newWordCount i64)

  (if (i32.eqz (get_local $length))
    (then (return))
  )

  ;; const newMemoryWordCount = Math.ceil[[offset + length] / 32]
  (set_local $newWordCount 
    (i64.div_u (i64.add (i64.const 31) (i64.add (i64.extend_u/i32 (get_local $offset)) (i64.extend_u/i32 (get_local $length))))
               (i64.const 32)))

  ;;if [runState.highestMem >= highestMem]  return
  (if (i64.le_u (get_local $newWordCount) (get_global $wordCount))
    (then (return))
  )

  ;; words * 3 + words ^2 / 512
  (set_local $cost
     (i64.add
       (i64.mul (get_local $newWordCount) (i64.const 3))
       (i64.div_u
         (i64.mul (get_local $newWordCount)
                  (get_local $newWordCount))
         (i64.const 512))))

  (call $useGas  (i64.sub (get_local $cost) (get_global $prevMemCost)))
  (set_global $prevMemCost (get_local $cost))
  (set_global $wordCount (get_local $newWordCount))

  ;; grow actual memory
  ;; the first 31704 bytes are guaranteed to be available
  ;; adjust for 32 bytes  - the maximal size of MSTORE write
  ;; TODO it should be current_memory * page_size
  (set_local $offset (i32.add (get_local $length) (i32.add (get_local $offset) (get_global $memstart))))
  (if (i32.gt_u (get_local $offset) (i32.mul (i32.const 65536) (current_memory)))
    (then
      (drop (grow_memory
        (i32.div_u (i32.add (i32.const 65535) (i32.sub (get_local $offset) (current_memory))) (i32.const 65536))))
    )
  )
)
  (func $main    (export "main")    (local $jump_dest i32) (local $jump_map_switch i32)    (set_local $jump_dest (i32.const -1))    (block $done      (loop $loop          (block $0
    (if
      (i32.eqz (get_global $init))
      (then
        (set_global $init (i32.const 1))
        (br $0))
      (else
        ;; the callback dest can never be in the first block
        (if (i32.eq (get_global $cb_dest) (i32.const 0)) 
          (then
            (unreachable)
          )
          (else 
            ;; return callback destination and zero out $cb_dest 
            (set_local $jump_map_switch (get_global $cb_dest)) 
            (set_global $cb_dest (i32.const 0))
            (br_table $0  (get_local $jump_map_switch))
          )))))(call $useGas (i64.const 24))(if (i32.gt_s (get_global $sp) (i32.const 32480))
                        (then (unreachable)))(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 64))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 0))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 64))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 0))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 1))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 268435456)(i64.const 0)(i64.const 1))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 350000))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $CALL)
(set_global $sp(i32.add(get_global $sp)(i32.const -192)))
(call $PUSH (i64.const 0)(i64.const 0)(i64.const 0)(i64.const 0))(set_global $sp(i32.add(get_global $sp)(i32.const 32)))
(call $SSTORE)
(set_global $sp(i32.add(get_global $sp)(i32.const -64)))
)))
)

Here is the fix for this specific issue: https://github.com/ewasm/evm2wasm/pull/276

jwasinger commented 6 years ago

Linting generated wast using Binaryen would be nice to have for CircleCI.