;;; -*- Mode:Common-Lisp; Package:L; Base:10 -*-
;;;
;;; il-instructions
;;;
;;; The intermediate-language instructions for the portable virtual machine, and their supporting datastructures.
;;;
;;; jsp 12-April-88
;;;



;;; il-instruction data structure
;;;
;;; This is used by the later phases of assembly, to generate binary code given a list of i-nodes.
;;; To install a new instruction, one simply needs to add a line to the definition of IL-INSTRUCTION-LIST,
;;; and re-run INSTALL-IL-INSTRUCTIONS.

(define-co il-instruction print-il-instruction
  ())

(defaccessor il-instruction 'keep :name)
(defaccessor il-instruction 'keep :branch?)
(defaccessor il-instruction 'keep :opposite)
(defaccessor il-instruction 'keep :nargs)
(defaccessor il-instruction 'keep :pos)
(defaccessor il-instruction 'keep :syntax)		; holds a representation of the instruction's expected syntax
(defaccessor il-instruction 'keep :defkind)		; iff the instruction is a definition, gives the implicit type
(defaccessor il-instruction 'keep :noflo?)		; true iff the instruction causes control-flow halt
(defaccessor il-instruction 'keep :processing)		; contains symbol name of function to call if instruction matched
(defaccessor il-instruction 'keep :c-fold)		; the constant-folding operation

(defun print-il-instruction (inst stream level)
  (if (or (null *print-level*)
	  (< level *print-level*))
      (progn
	(if (< 1 level)
	    (progn
	      (format stream "~%")
	      (dotimes (i level) (format stream " "))))
	(progn
	  (format stream "#<il-instruction ~a " (il-instruction-name inst))
	  (and (il-instruction-opposite inst)
	       (format stream "(~a) " (il-instruction-opposite inst)))
	  (and (il-instruction-pos inst)
	       (format stream " :pos ~a" (il-instruction-pos inst)))
	  (and (il-instruction-syntax inst)
	       (format stream " :syntax ~a" (il-instruction-syntax inst)))
	  (format stream ">"))
	)
      )
  '())

(defun il-conditional-branch? (inst)
  (and inst (il-instruction-branch? inst) (il-instruction-opposite inst)))

(defun il-unconditional-branch? (inst)
  (and inst (il-instruction-branch? inst) (null (il-instruction-opposite inst))))



;;; il-instructions
;;;
;;; This is a hash table of the available L instructions, along with their coding.

(defvar il-instructions ())


;;; il-instruction-list
;;;
;;; This is the human-readable and -modifiable form of the hash table.  Upon start-up, the system
;;; reads in this list, and installs each element into IL-INSTRUCTIONS, the hash table.

;;; The syntax fields have the following interpretation.
;;;
;;; When an instruction is matched by position, an attempt is made to match the syntax string of the
;;; instruction.  The syntax string consists of a number of lists, each of which is tried in turn until
;;; a match is found [a slight lie].  The following tokens are valid syntacital descriptors: i (ignore
;;; element), r (element is read), w (element is written), d (element is defined), l (element is referenced
;;; as a label), s (element is a source statement), ty (element is a type), v (element is a value),
;;; and + (repeat previous element indefinitely). 
;;; cx indicates a condition for IF statement.
;;; Sublists are also supported.

(defvar il-instruction-list
	'((:name activate   :pos 0 :syntax ((i r)))
	  (:name adc        :pos 1 :syntax ((w i r r)))
	  (:name add        :pos 1 :syntax ((w i r r))          :c-fold +)
	  (:name alloc      :pos 1 :syntax ((w i r)))
	  (:name and        :pos 1 :syntax ((w i r r))          :c-fold logand)
	  (:name ash        :pos 1 :syntax ((w i r r))          :c-fold ash)
	  (:name argdef     :pos 0 :syntax ((i (d ty) +))	:defkind arg)
	  (:name blt        :pos 1 :syntax ((r i r)))
	  (:name call       :pos 1 :syntax ((w i r +)))
	  (:name cmp        :pos 1 :syntax ((w i r r)))
	  (:name comment    :pos 0 :syntax ((i +)))
	  (:name constdef   :pos 0 :syntax ((i (d ty v) +))	:defkind const)
	  (:name div        :pos 1 :syntax ((w i r r))          :c-fold /)
	  (:name deactivate :pos 0 :syntax ((i))		:noflo? t)
	  (:name elt        :pos 1 :syntax ((w i r r)))		; ???
	  (:name fundef     :pos 0 :syntax ((i (d ty s) +))	:defkind fn)
	  (:name goto       :pos 0 :syntax ((i l))		:noflo? t)
	  (:name if         :pos 0 :syntax ((i cx r l [ l ])))
	  (:name label      :pos 0 :syntax ((i d +))		:defkind label)
	  (:name ldb        :pos 1 :syntax ((w i r r r)))	; ???
	  (:name load       :pos 1 :syntax ((w r i r)))		; ???
	  (:name load-cc    :pos 1 :syntax ((w i)))
	  (:name localdef   :pos 0 :syntax ((i (d ty v) +))	:defkind local)
	  (:name lock       :pos 1 :syntax ((w i r)))
	  (:name lsh        :pos 1 :syntax ((w i r r))          :c-fold lsh)
	  (:name mod        :pos 1 :syntax ((w i r r))          :c-fold mod)
	  (:name move       :pos 1 :syntax ((w i r)))
	  (:name mult       :pos 1 :syntax ((w i r r))          :c-fold *)
	  (:name neg        :pos 1 :syntax ((w i r))            :c-fold (lisp:lambda (a) (- a)))
	  (:name nop        :pos 0 :syntax ((i)))
	  (:name not        :pos 1 :syntax ((w i r))            :c-fold lognot)
	  (:name or         :pos 1 :syntax ((w i r r))          :c-fold logor)
	  (:name reserve    :pos 0 :syntax ((i (i +)))		:processing process-reserve)
	  (:name results    :pos 0 :syntax ((i w +)))
	  (:name return     :pos 0 :syntax ((i r +)))
	  (:name rot        :pos 1 :syntax ((w i r r))          :c-fold rot)
	  (:name sbc        :pos 1 :syntax ((w i r r)))
	  (:name self       :pos 1 :syntax ((w i)))
	  (:name set-elt    :pos 1 :syntax ((w i r r)))		; ???
	  (:name static     :pos 0 :syntax ((i +))              :processing process-static)
	  (:name stb        :pos 1 :syntax ((w i r r r)))	; ???
	  (:name store      :pos 1 :syntax ((w i r r)))		; ???
	  (:name store-cc   :pos 1 :syntax ((w i)))
	  (:name sub        :pos 1 :syntax ((w i r r))          :c-fold -)
	  (:name suspend    :pos 0 :syntax ((i))		:noflo? t)
	  (:name tempdef    :pos 0 :syntax ((i (d ty) +))	:defkind temp)
	  (:name test       :pos 1 :syntax ((w i r r)))
	  (:name unlock     :pos 1 :syntax ((w i r)))
	  (:name xor        :pos 1 :syntax ((w i r r))          :c-fold logxor)
	  ))


(defvar *il-move-instruction*     '())
(defvar *il-goto-instruction*     '())
(defvar *il-return-instruction*   '())
(defvar *il-comment-instruction*  '())
(defvar *il-label-instruction*    '())
(defvar *il-if-instruction*       '())
(defvar *il-label-instruction*    '())
(defvar *il-fundef-instruction*   '())
(defvar *il-argdef-instruction*   '())
(defvar *il-tempdef-instruction*  '())
(defvar *il-localdef-instruction* '())
(defvar *il-constdef-instruction* '())


;;; initialize-il-instructions
;;;
;;; This is the installation function.  Given a list of L instructions, it creates a hash-table entry
;;; for each element of the list.  The list is taken from IL-INSTRUCTION-LIST, and the table is placed
;;; in IL-INSTRUCTIONS.

(defun initialize-il-instructions ()
  "Initializes the IL instruction hash table from IL-INSTRUCTION-LIST.  Also sets the various global
variables for specific instructions such as *il-move-instruction*.  Returns OK."
  (if il-instructions
      (clrhash il-instructions)			; clear out old stuff
      (setq il-instructions (make-hash-table)))
  (dolist (inst il-instruction-list)
    (let ((inst-obj (apply #'new-il-instruction inst)))
      (puthash (il-instruction-name inst-obj) inst-obj il-instructions)))

  (setq *il-move-instruction*    (get-il-instruction 'move))
  (setq *il-goto-instruction*    (get-il-instruction 'goto))
  (setq *il-return-instruction*  (get-il-instruction 'return))
  (setq *il-comment-instruction* (get-il-instruction 'comment))
  (setq *il-label-instruction*   (get-il-instruction 'label))
  (setq *il-if-instruction*      (get-il-instruction 'if))
  (setq *il-label-instruction*   (get-il-instruction 'label))
  (setq *il-fundef-instruction*    (get-il-instruction 'fundef))
  (setq *il-argdef-instruction*    (get-il-instruction 'argdef))
  (setq *il-tempdef-instruction*   (get-il-instruction 'tempdef))
  (setq *il-localdef-instruction*  (get-il-instruction 'localdef))
  (setq *il-constdef-instruction*  (get-il-instruction 'constdef))
  
  'ok
  )

;;; get-il-instruction
;;;
;;; Given a symbol, this trys to find an L instruction with that name.  If it fails, then it generates an error.

(defun get-il-instruction (name &optional (error? '()))
  (let ((inst (gethash name il-instructions))) 
     (or inst
	 (and error?
	      (or
		(progn
		  (error-message 2 "Unable to find L instruction ~a, replacing with NOP" name)
		  (gethash 'nop il-instructions))
		(make-il-instruction :name 'foo :ix (ix #b000000)))))))






