src/wam/compiler/7-rendering.lisp @ 9d42a27624fd
`tree-collect` is slow and conses a ton
| author | Steve Losh <steve@stevelosh.com> |
|---|---|
| date | Fri, 15 Jul 2016 20:43:32 +0000 |
| parents | a095d20eeebc |
| children | ec2fab887b0f |
(in-package #:bones.wam) ;;;; .-,--. . ;;;; `|__/ ,-. ,-. ,-| ,-. ,-. . ,-. ,-. ;;;; )| \ |-' | | | | |-' | | | | | | ;;;; `' ` `-' ' ' `-^ `-' ' ' ' ' `-| ;;;; ,| ;;;; `' ;;; Rendering is the act of taking the friendly list-of-instructions format and ;;; actually converting it to raw-ass bytes and storing it in an array. (defun check-instruction (opcode arguments) (assert (= (length arguments) (1- (instruction-size opcode))) () "Cannot push opcode ~A with ~D arguments ~S, it requires exactly ~D." (opcode-name opcode) (length arguments) arguments (1- (instruction-size opcode)))) (defun* code-push-instruction ((store generic-code-store) (opcode opcode) (arguments list) (address code-index)) "Push the given instruction into `store` at `address`. `arguments` should be a list of `code-word`s. Returns how many words were pushed. " (:returns instruction-size) (check-instruction opcode arguments) (setf (aref store address) opcode (subseq store (1+ address)) arguments) (instruction-size opcode)) (defun* render-opcode ((opcode-designator keyword)) (:returns opcode) (ecase opcode-designator (:get-structure +opcode-get-structure+) (:get-variable-local +opcode-get-variable-local+) (:get-variable-stack +opcode-get-variable-stack+) (:get-value-local +opcode-get-value-local+) (:get-value-stack +opcode-get-value-stack+) (:put-structure +opcode-put-structure+) (:put-variable-local +opcode-put-variable-local+) (:put-variable-stack +opcode-put-variable-stack+) (:put-value-local +opcode-put-value-local+) (:put-value-stack +opcode-put-value-stack+) (:subterm-variable-local +opcode-subterm-variable-local+) (:subterm-variable-stack +opcode-subterm-variable-stack+) (:subterm-value-local +opcode-subterm-value-local+) (:subterm-value-stack +opcode-subterm-value-stack+) (:subterm-void +opcode-subterm-void+) (:put-constant +opcode-put-constant+) (:get-constant +opcode-get-constant+) (:subterm-constant +opcode-subterm-constant+) (:get-list +opcode-get-list+) (:put-list +opcode-put-list+) (:get-lisp-object +opcode-get-lisp-object+) (:put-lisp-object +opcode-put-lisp-object+) (:jump +opcode-jump+) (:call +opcode-call+) (:dynamic-jump +opcode-dynamic-jump+) (:dynamic-call +opcode-dynamic-call+) (:proceed +opcode-proceed+) (:allocate +opcode-allocate+) (:deallocate +opcode-deallocate+) (:done +opcode-done+) (:try +opcode-try+) (:retry +opcode-retry+) (:trust +opcode-trust+) (:cut +opcode-cut+))) (defun* render-argument (argument) (:returns code-word) (etypecase argument (null 0) ; ugly choice point args that'll be filled later... (register (register-number argument)) ; bytecode just needs register numbers (t argument))) ; everything else just gets shoved right into the array (defun* render-bytecode ((store generic-code-store) (instructions circle) (start code-index) (limit code-index)) "Render `instructions` (a circle) into `store` starting at `start`. Bail if ever pushed beyond `limit`. Return the total number of code words rendered. " (let ((previous-jump nil)) (flet ((fill-previous-jump (address) (when previous-jump (setf (aref store (1+ previous-jump)) address)) (setf previous-jump address))) (loop :with address = start ;; Render the next instruction :for (opcode-designator . arguments) :in (circle-to-list instructions) :for opcode = (render-opcode opcode-designator) :for size = (instruction-size opcode) :summing size ;; Make sure we don't run past the end of our section. :when (>= (+ size address) limit) :do (error "Code store exhausted, game over.") :do (code-push-instruction store opcode (mapcar #'render-argument arguments) address) ;; We need to fill in the addresses for the choice point jumping ;; instructions. For example, when we have TRY ... TRUST, the TRUST ;; needs to patch its address into the TRY instruction. ;; ;; I know, this is ugly, sorry. :when (member opcode-designator '(:try :retry :trust)) :do (fill-previous-jump address) ;; look, don't judge me, i told you i know its bad :do (incf address size))))) (defun* render-query ((wam wam) (instructions circle)) (render-bytecode (wam-code wam) instructions 0 +maximum-query-size+)) (defun* mark-label ((wam wam) (functor symbol) (arity arity) (address code-index)) "Set the code label `functor`/`arity` to point at `address`." (setf (wam-code-label wam functor arity) address)) (defun* render-rules ((wam wam) (functor symbol) (arity arity) (instructions circle)) ;; Before we render the instructions, make the label point at where they're ;; about to go. (mark-label wam functor arity (wam-code-pointer wam)) (incf (wam-code-pointer wam) (render-bytecode (wam-code wam) instructions (wam-code-pointer wam) (array-total-size (wam-code wam)))))