git.fiddlerwoaroof.com
cl-wasm.lisp
19b77c36
 (in-package :cl-wasm)
 
f393694d
 (declaim (inline signed invsigned))
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun signed (n i)
     (if (and (<= 0 i)
              (< i (expt 2 (1- n))))
         i
         (- i (expt 2 n))))
   (defun invsigned (n i)
     (if (< i 0)
         (+ i (expt 2 n))
         i)))
19b77c36
 
 (defgeneric run-inst (client stack locals instruction argument)
   (:method (client (stack list) (locals vector) (instruction (eql :get-local)) (argument number))
     (cons (aref locals argument)
           stack)))
 
 (defmacro define-numeric-instruction (suffix (&optional (arg '(argument null))) &body body)
   (let ((i32-name (alexandria:make-keyword (format nil "~a.~a" :i32 suffix)))
         (i64-name (alexandria:make-keyword (format nil "~a.~a" :i64 suffix)))
         (f32-name (alexandria:make-keyword (format nil "~a.~a" :f32 suffix)))
         (f64-name (alexandria:make-keyword (format nil "~a.~a" :f64 suffix))))
     (flet ((get-argument (type)
              (case type
                (:i32 (list (car arg)
                            (case (cadr arg)
                              (:type 'integer)
                              (t (cadr arg)))))
                (:i64 (list (car arg)
                            (case (cadr arg)
                              (:type 'integer)
                              (t (cadr arg)))))
                (:f32 (list (car arg)
                            (case (cadr arg)
                              (:type 'single-float)
                              (t (cadr arg)))))
                (:f64 (list (car arg)
                            (case (cadr arg)
                              (:type 'double-float)
                              (t (cadr arg))))))))
       `(macrolet ((numeric-dispatch (&body forms)
                     (cons 'or forms)))
          (defmethod run-inst (client (stack list) (locals vector) (inst (eql ,i32-name)) ,(get-argument :i32))
f393694d
            (declare (optimize (speed 1) (safety 1)))
19b77c36
            (macrolet ((numeric-case (type &body body)
                         (when (eql type :i32)
                           (cons 'progn body))))
              (let ((numeric-type '(integer 0 #.(1- (expt 2 32))))
                    (size 32))
                (declare (ignorable numeric-type size))
                ,@body)))
          (defmethod run-inst (client (stack list) (locals vector) (inst (eql ,i64-name)) ,(get-argument :i64))
f393694d
            (declare (optimize (speed 1) (safety 1)))
19b77c36
            (macrolet ((numeric-case (type &body body)
                         (when (eql type :i64)
                           (cons 'progn body))))
f393694d
              (let ((numeric-type '(integer 0 #.(1- (expt 2 64))))
19b77c36
                    (size 64))
                (declare (ignorable numeric-type size))
                ,@body)))
          (defmethod run-inst (client (stack list) (locals vector) (inst (eql ,f32-name)) ,(get-argument :f32))
f393694d
            (declare (optimize (speed 1) (safety 1)))
19b77c36
            (macrolet ((numeric-case (type &body body)
                         (when (eql type :f32)
                           (cons 'progn body))))
              (let ((numeric-type 'single-float)
                    (size 32))
                (declare (ignorable numeric-type size))
                ,@body)))
          (defmethod run-inst (client (stack list) (locals vector) (inst (eql ,f64-name)) ,(get-argument :f64))
f393694d
            (declare (optimize (speed 1) (safety 1)))
19b77c36
            (macrolet ((numeric-case (type &body body)
                         (when (eql type :f64)
                           (cons 'progn body))))
              (let ((numeric-type 'double-float)
                    (size 64))
                (declare (ignorable numeric-type size))
                ,@body)))))))
 
 (define-numeric-instruction add ()
   (let ((result (+ (coerce (pop stack) numeric-type)
                     (coerce (pop stack) numeric-type))))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod result #.(expt 2 32)))
            (numeric-case :i64 (mod result #.(expt 2 64)))
            (numeric-case :f32 result)
            (numeric-case :f64 result))
           stack)))
 
 (define-numeric-instruction mul ()
   (let ((result (* (coerce (pop stack) numeric-type)
                    (coerce (pop stack) numeric-type))))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod result #.(expt 2 32)))
            (numeric-case :i64 (mod result #.(expt 2 64)))
            (numeric-case :f32 result)
            (numeric-case :f64 result))
           stack)))
 
 (define-numeric-instruction const ((arg :type))
   (cons (coerce arg numeric-type) 
         stack))
 
 (define-numeric-instruction sub ()
   (let ((i2 (pop stack))
         (i1 (pop stack)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (+ (- (coerce i1 numeric-type)
                                           (coerce i2 numeric-type))
                                        #.(expt 2 32))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (+ (- (coerce i1 numeric-type)
                                           (coerce i2 numeric-type))
                                        #.(expt 2 64))
                                    #.(expt 2 64)))
            (numeric-case :f32 (- (coerce i1 numeric-type)
                                   (coerce i2 numeric-type)))
            (numeric-case :f64 (- (coerce i1 numeric-type)
                                   (coerce i2 numeric-type))))
           stack)))
 
 (define-numeric-instruction div_u ()
   (let ((i2 (pop stack))
         (i1 (pop stack)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (truncate (coerce i1 numeric-type)
                                              (coerce i2 numeric-type))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (truncate (coerce i1 numeric-type)
                                              (coerce i2 numeric-type))
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating div not implemented"))
            (numeric-case :f64 (error "floating div not implemented")))
           stack)))
 
 (define-numeric-instruction div_s ()
   (let* ((i2 (coerce (pop stack) numeric-type))
f393694d
          (j2 (signed size i2))
19b77c36
          (i1 (coerce (pop stack) numeric-type))
f393694d
          (j1 (signed size i1)))
19b77c36
     (declare (ignorable i1 i2 j1 j2))
     (cons (numeric-dispatch
f393694d
            (numeric-case :i32 (if (/= (truncate i1 i2) #.(expt 2 31))
                                   (invsigned 32 (truncate j1 j2))
19b77c36
                                   (error "undefined result")))
            (numeric-case :i64 (if (/= (truncate (coerce i1 numeric-type)
                                                 (coerce i2 numeric-type))
                                       #.(expt 2 63))
f393694d
                                   (invsigned 64 (truncate j1 j2))
19b77c36
                                   (error "undefined result")))
            (numeric-case :f32 (error "floating div not implemented"))
            (numeric-case :f64 (error "floating div not implemented")))
           stack)))
 
 (define-numeric-instruction rem_u ()
   (let ((i2 (pop stack))
         (i1 (pop stack)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (- (coerce i1 numeric-type)
                                        (* (coerce i2 numeric-type)
                                           (truncate i1 i2)))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (- (coerce i1 numeric-type)
                                        (* (coerce i2 numeric-type)
                                           (truncate i1 i2)))
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction rem_s ()
   (let* ((i2 (pop stack))
          (j2 (signed size i2))
          (i1 (pop stack))
          (j1 (signed size i1)))
     (declare (ignorable i1 i2 j1 j2))
     (cons (numeric-dispatch
f393694d
            (numeric-case :i32 (mod (- j1 (* j2
                                             (truncate j1 j2)))
19b77c36
                                    #.(expt 2 32)))
f393694d
            (numeric-case :i64 (mod (- j1 (* j2
                                             (truncate j1 j2)))
19b77c36
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction rem_s ()
   (let* ((i2 (pop stack))
          (j2 (signed size i2))
          (i1 (pop stack))
          (j1 (signed size i1)))
     (declare (ignorable i1 i2 j1 j2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (- (coerce j1 numeric-type)
                                        (* (coerce j2 numeric-type)
                                           (truncate j1 j2)))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (- (coerce j1 numeric-type)
                                        (* (coerce j2 numeric-type)
                                           (truncate j1 j2)))
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction and ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (logand #.(1- (expt 2 32))
                                       (logand i1 i2)))
            (numeric-case :i64 (logand #.(1- (expt 2 64))
                                       (logand i1 i2)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction or ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (logand #.(1- (expt 2 32))
                                       (logior i1 i2)))
            (numeric-case :i64 (logand #.(1- (expt 2 64))
                                       (logior i1 i2)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction xor ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (logand #.(1- (expt 2 32))
                                       (logxor i1 i2)))
            (numeric-case :i64 (logand #.(1- (expt 2 64))
                                       (logxor i1 i2)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
f393694d
 (define-numeric-instruction shl ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (ash i1 i2)
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (ash i1 i2)
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction shr_u ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (ash i1 (- i2))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (ash i1 (- i2))
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
 (define-numeric-instruction shr_s ()
   (let* ((i2 (coerce (pop stack)
                      numeric-type))
          (i1 (coerce (pop stack)
                      numeric-type)))
     (declare (ignorable i1 i2))
     (cons (numeric-dispatch
            (numeric-case :i32 (mod (ash (logand -1 i1)
                                         (- i2))
                                    #.(expt 2 32)))
            (numeric-case :i64 (mod (ash (logand -1 i1)
                                         (- i2))
                                    #.(expt 2 64)))
            (numeric-case :f32 (error "floating rem not implemented"))
            (numeric-case :f64 (error "floating rem not implemented")))
           stack)))
 
19b77c36
 (fw.lu:defclass+ tracer ()
   ((%level :initarg :level :accessor level)))
 (fw.lu:defclass+ wat-emitter ()
   ((%level :initarg :level :accessor level)))
 
 (defvar *client*
   (tracer 0))
 
 (defmethod run-inst :around ((client tracer) stack locals instruction argument)
   (format t "~&~vt> ~s ~s ~s~%~vt~8t|~{~s~^ ~}|"
           (level client) locals instruction argument
           (level client) (reverse stack))
   (incf (level client))
   (unwind-protect
        (let ((out (call-next-method)))
          (prog1 out
            (format t "~&~vt<~8t|~{~s~^ ~}|~%"
                    (level client) (reverse out))))
     (decf (level client))))
 
 (defmethod run-inst :around ((client wat-emitter) stack locals instruction argument)
   (incf (level client))
   (unwind-protect
        (let ((out (call-next-method)))
          (let ((*print-case* :downcase))
            (format t "~&~4t~a~:[~; ~:*~a~]" instruction argument))
          out)
     (decf (level client))))
 
 (defgeneric pre-interpret (client)
   (:method (client)))
 (defgeneric post-interpret (client)
   (:method (client)))
 
 (defmethod pre-interpret ((client wat-emitter))
   (format t "~&(module (func $it (result i32)"))
 (defmethod post-interpret ((client wat-emitter))
   (format t ")
   (export \"it\" (func $it)))~%"))
 
 (defun interpret (instructions locals)
   (pre-interpret *client*)
   (let ((stack ()))
     (loop for (instruction argument) in instructions
           do (setf stack
                    (run-inst *client* stack locals instruction argument)))
     (post-interpret *client*)
     stack))
 
 (defun test-responder ()
   (spinneret:with-html-string
     (:html
      (:body
       (:script :src "https://localhost/~elangley/libwabt.js")
f393694d
       (:script
        (ps:ps
          (let* ((ws (ps:new (-web-socket "ws://localhost:5001/ws")))
                 (compile-wat (lambda (m)
                                (lambda (wabt)
                                  (ps:chain ws
                                            (send
                                             (+ "result "
                                                (ps:chain
                                                 (setf (ps:@ window module)
                                                       (ps:new
                                                        ((ps:@ -web-assembly -instance)
                                                         (ps:new
                                                          ((ps:@ -web-assembly -module)
                                                           (ps:chain wabt
                                                                     (parse-wat "foo.wast"
                                                                                (ps:@ m data))
                                                                     (to-binary
                                                                      (ps:create log ps:t
                                                                                 write_debug_names ps:t))
                                                                     buffer))))))
                                                 exports
                                                 (it))))))))
                 (message-handler (lambda (m)
                                    (ps:chain console (log (ps:@ m data)))
                                    (ps:chain (ps:new (-wabt-module))
                                              (then (compile-wat m))))))
            (ps:chain ws
                      (add-event-listener "message" message-handler))
            (ps:chain ws
                      (add-event-listener "close"
                                          (lambda (_)
                                            (setf ws (ps:new (-web-socket "ws://localhost:5001/ws")))
                                            (ps:chain ws
                                                      (add-event-listener "message" message-handler))))))))))))
 
 (defclass wasm-session (hunchensocket:websocket-resource)
   ((name :initarg :name :reader name)))
 
 (defvar *wasm-sessions*
   (make-array 10 :fill-pointer 0 :adjustable t))
 
 (defmethod hunchensocket:text-message-received ((resource wasm-session) client message)
   (format t "~&in: ~a~%" message))
 
 (defun find-wasm-session (request)
   (or (find (hunchentoot:script-name request)
             *wasm-sessions*
             :test #'string=
             :key 'name)
       (fw.lu:prog1-bind (session (make-instance 'wasm-session 
                                                 :name (hunchentoot:script-name request)))
         (vector-push-extend session
                             *wasm-sessions*))))
 
 (pushnew 'find-wasm-session
          hunchensocket:*websocket-dispatch-table*)
 
 (defun broadcast (session message)
   (loop for peer in (hunchensocket:clients session)
         do (hunchensocket:send-text-message peer message)))
 
 (defclass my-acceptor (hunchensocket:websocket-acceptor)
   ())
 
 (defmethod hunchentoot:acceptor-dispatch-request ((acceptor my-acceptor) request)
   (if (string= "/" (hunchentoot:script-name request))
       (test-responder)
       (call-next-method)))
19b77c36
 
 (defvar *app*)
 (defvar *handler*)
f393694d
 
19b77c36
 (defun demo ()
f393694d
   (let ((app (make-instance 'my-acceptor :port 5001)))
     (values (hunchentoot:start app) 
19b77c36
             app)))
 
 (defun go* ()
   (setf (values *handler* *app*)
         (demo)))
f393694d
 
 #|
 i32.clz
 i32.ctz
 i32.popcnt
 i32.shl
 i32.shr_s
 i32.shr_u
 i32.rotl
 i32.rotr
 ​	
 i64.clz
 i64.ctz
 i64.popcnt
 i64.shl
 i64.shr_s
 i64.shr_u
 i64.rotl
 i64.rotr
 ​	
 f32.abs
 f32.neg
 f32.ceil
 f32.floor
 f32.trunc
 f32.nearest
 f32.sqrt
 f32.div
 f32.min
 f32.max
 f32.copysign
 ​	
 f64.abs
 f64.neg
 f64.ceil
 f64.floor
 f64.trunc
 f64.nearest
 f64.sqrt
 f64.div
 f64.min
 f64.max
 f64.copysign
 ​	
 i32.eqz
 i32.eq
 i32.ne
 i32.lt_s
 i32.lt_u
 i32.gt_s
 i32.gt_u
 i32.le_s
 i32.le_u
 i32.ge_s
 i32.ge_u
 ​	
 i64.eqz
 i64.eq
 i64.ne
 i64.lt_s
 i64.lt_u
 i64.gt_s
 i64.gt_u
 i64.le_s
 i64.le_u
 i64.ge_s
 i64.ge_u
 ​	
 f32.eq
 f32.ne
 f32.lt
 f32.gt
 f32.le
 f32.ge
 ​	
 f64.eq
 f64.ne
 f64.lt
 f64.gt
 f64.le
 f64.ge
 ​	
 
 i32.wrap_i64
 i32.trunc_f32_s
 i32.trunc_f32_u
 i32.trunc_f64_s
 i32.trunc_f64_u
 i64.extend_i32_s
 i64.extend_i32_u
 i64.trunc_f32_s
 i64.trunc_f32_u
 i64.trunc_f64_s
 i64.trunc_f64_u
 f32.convert_i32_s
 f32.convert_i32_u
 f32.convert_i64_s
 f32.convert_i64_u
 f32.demote_f64
 f64.convert_i32_s
 f64.convert_i32_u
 f64.convert_i64_s
 f64.convert_i64_u
 f64.promote_f32
 i32.reinterpret_f32
 i64.reinterpret_f64
 f32.reinterpret_i32
 f64.reinterpret_i64
 |#