summaryrefslogtreecommitdiff
path: root/cgen/utils-sim.scm
diff options
context:
space:
mode:
Diffstat (limited to 'cgen/utils-sim.scm')
-rw-r--r--cgen/utils-sim.scm955
1 files changed, 955 insertions, 0 deletions
diff --git a/cgen/utils-sim.scm b/cgen/utils-sim.scm
new file mode 100644
index 00000000000..e0951adb07a
--- /dev/null
+++ b/cgen/utils-sim.scm
@@ -0,0 +1,955 @@
+; Generic simulator application utilities.
+; Copyright (C) 2000 Red Hat, Inc.
+; This file is part of CGEN.
+; See file COPYING.CGEN for details.
+
+; The cache-addr? method.
+; Return #t if the hardware element's address is stored in the scache buffer.
+; This saves doing the index calculation during semantic processing.
+
+(method-make!
+ <hardware-base> 'cache-addr?
+ (lambda (self)
+ (and (with-scache?)
+ (has-attr? self 'CACHE-ADDR)))
+)
+
+(define (hw-cache-addr? hw) (send hw 'cache-addr?))
+
+; The needed-iflds method.
+; Return list of ifields needed during semantic execution by hardware element
+; SELF referenced by <operand> OP in <sformat> SFMT.
+
+(method-make!
+ <hardware-base> 'needed-iflds
+ (lambda (self op sfmt)
+ (list (op-ifield op)))
+)
+
+(method-make!
+ <hw-register> 'needed-iflds
+ (lambda (self op sfmt)
+ (list (op-ifield op)))
+; Instead of the following, we now arrange to store the ifield in the
+; argbuf, even for CACHE-ADDR operands. This way, the ifield values
+; (register numbers, etc.) remain available during semantics tracing.
+; (if (hw-cache-addr? self)
+; nil
+; (list (op-ifield op))))
+)
+
+; For addresses this is none because we make our own copy of the ifield
+; [because we want to use a special type].
+
+(method-make!
+ <hw-address> 'needed-iflds
+ (lambda (self op sfmt)
+ nil)
+)
+
+(define (hw-needed-iflds hw op sfmt) (send hw 'needed-iflds op sfmt))
+
+; Return a list of ifields of <operand> OP that must be recorded in ARGBUF
+; for <sformat> SFMT.
+; ??? At the moment there can only be at most one, but callers must not
+; assume this.
+
+(define (op-needed-iflds op sfmt)
+ (let ((indx (op:index op)))
+ (if (and (eq? (hw-index:type indx) 'ifield)
+ (not (= (ifld-length (hw-index:value indx)) 0)))
+ (hw-needed-iflds (op:type op) op sfmt)
+ nil))
+)
+
+; Operand extraction (ARGBUF) support code.
+;
+; Any operand that uses a non-empty ifield needs extraction support.
+; Normally we just record the ifield's value. However, in cases where
+; hardware elements have CACHE-ADDR specified or where the mode of the
+; hardware index isn't compatible with the mode of the decoded ifield
+; (this can happen for pc-relative instruction address), we need to record
+; something else.
+
+; Return a boolean indicating if <operand> OP needs any extraction processing.
+
+(define (op-extract? op)
+ (let* ((indx (op:index op))
+ (extract?
+ (if (derived-operand? op)
+ (any-true? (map op-extract? (derived-args op)))
+ (and (eq? (hw-index:type indx) 'ifield)
+ (not (= (ifld-length (hw-index:value indx)) 0))))))
+ (logit 4 "op-extract? op=" (obj:name op) " =>" extract? "\n")
+ extract?)
+)
+
+; Return a list of operands that need special extraction processing.
+; SFMT is an <sformat> object.
+
+(define (sfmt-extracted-operands sfmt)
+ (let ((in-ops (sfmt-in-ops sfmt))
+ (out-ops (sfmt-out-ops sfmt)))
+ (let ((ops (append (find op-extract? in-ops)
+ (find op-extract? out-ops))))
+ (nub ops obj:name)))
+)
+
+; Return a list of ifields that are needed by the semantic code.
+; SFMT is an <sformat> object.
+; ??? This redoes a lot of the calculation that sfmt-extracted-operands does.
+
+(define (sfmt-needed-iflds sfmt)
+ (let ((in-ops (sfmt-in-ops sfmt))
+ (out-ops (sfmt-out-ops sfmt)))
+ (let ((ops (append (find op-extract? in-ops)
+ (find op-extract? out-ops))))
+ (nub (apply append (map (lambda (op)
+ (op-needed-iflds op sfmt))
+ ops))
+ obj:name)))
+)
+
+; Sformat argument buffer.
+;
+; This contains the details needed to create an argument buffer `fields' union
+; entry for the containing sformats.
+
+(define <sformat-argbuf>
+ (class-make '<sformat-argbuf>
+ '(<ident>)
+ ; From <ident>:
+ ; - NAME is derived from one of the containing sformats.
+ '(
+ ; List of structure elements.
+ ; Each element is ("var name" "C type" bitsize).
+ ; The list is sorted by decreasing size, then C type,
+ ; then var name.
+ elms
+ )
+ nil)
+)
+
+(define-getters <sformat-argbuf> sbuf (sfmts elms))
+
+; Subroutine of -sfmt-contents to return an ifield element.
+; The result is ("var-name" "C-type" bitsize).
+
+(define (-sfmt-ifld-elm f sfmt)
+ (let ((real-mode (mode-real-mode (ifld-decode-mode f))))
+ (list (gen-sym f)
+ (mode:c-type real-mode)
+ (mode:bits real-mode)))
+)
+
+; sbuf-elm method.
+; The result is ("var-name" "C-type" approx-bitsize) or #f if unneeded.
+; For the default case we use the ifield as is, which is computed elsewhere.
+
+(method-make!
+ <hardware-base> 'sbuf-elm
+ (lambda (self op ifmt)
+ #f)
+)
+
+(method-make!
+ <hw-register> 'sbuf-elm
+ (lambda (self op ifmt)
+ (if (hw-cache-addr? self)
+ (list (gen-sym (op:index op))
+ (string-append (gen-type self) "*")
+ ; Use 64 bits for size. Doesn't really matter, just put them
+ ; near the front.
+ 64)
+ #f))
+)
+
+; We want to use ADDR/IADDR in ARGBUF for addresses
+
+(method-make!
+ <hw-address> 'sbuf-elm
+ (lambda (self op ifmt)
+ (list (gen-sym (op:index op))
+ "ADDR"
+ ; Use 64 bits for size. Doesn't really matter, just put them
+ ; near the front.
+ 64))
+)
+
+(method-make!
+ <hw-iaddress> 'sbuf-elm
+ (lambda (self op ifmt)
+ (list (gen-sym (op:index op))
+ "IADDR"
+ ; Use 64 bits for size. Doesn't really matter, just put them
+ ; near the front.
+ 64))
+)
+
+; Subroutine of -sfmt-contents to return an operand element.
+; These are in addition (or instead of) the actual ifields.
+; This is also used to compute definitions of local vars needed in the
+; !with-scache case.
+; The result is ("var-name" "C-type" approx-bitsize) or #f if unneeded.
+
+(define (sfmt-op-sbuf-elm op sfmt)
+ (send (op:type op) 'sbuf-elm op sfmt)
+)
+
+; Subroutine of compute-sformat-bufs! to compute list of structure elements
+; needed by <sformat> SFMT.
+; The result is
+; (SFMT ("var-name1" "C-type1" size1) ("var-name2" "C-type2" size2) ...)
+; and is sorted by decreasing size, then C type, then variable name
+; (as <sformat-argbuf> wants it).
+
+(define (-sfmt-contents sfmt)
+ (let ((needed-iflds (sfmt-needed-iflds sfmt))
+ (extracted-ops (sfmt-extracted-operands sfmt))
+ (in-ops (sfmt-in-ops sfmt))
+ (out-ops (sfmt-out-ops sfmt))
+ (sort-elms (lambda (a b)
+ ; Sort by descending size, then ascending C type, then
+ ; ascending name.
+ (cond ((> (caddr a) (caddr b))
+ #t)
+ ((= (caddr a) (caddr b))
+ (cond ((string<? (cadr a) (cadr b))
+ #t)
+ ((string=? (cadr a) (cadr b))
+ (string<? (car a) (car b)))
+ (else
+ #f)))
+ (else
+ #f))))
+ )
+ (cons sfmt
+ (sort
+ ; Compute list of all things we need to record at extraction time.
+ (find (lambda (x)
+ ; Discard #f entries, they indicate "unneeded".
+ x)
+ (append
+ (map (lambda (f)
+ (-sfmt-ifld-elm f sfmt))
+ needed-iflds)
+ (map (lambda (op)
+ (sfmt-op-sbuf-elm op sfmt))
+ extracted-ops)
+ (cond ((with-any-profile?)
+ (append
+ ; Profiling support. ??? This stuff is in flux.
+ (map (lambda (op)
+ (sfmt-op-profile-elm op sfmt #f))
+ (find op-profilable? in-ops))
+ (map (lambda (op)
+ (sfmt-op-profile-elm op sfmt #t))
+ (find op-profilable? out-ops))))
+ (else
+ (append)))))
+ sort-elms)))
+)
+
+; Return #t if ELM-LIST is a subset of SBUF.
+; SBUF is an <sformat-argbuf> object.
+
+(define (-sbuf-subset? elm-list sbuf)
+ ; We take advantage of the fact that elements in each are already sorted.
+ ; FIXME: Can speed up.
+ (let loop ((elm-list elm-list) (sbuf-elm-list (sbuf-elms sbuf)))
+ (cond ((null? elm-list)
+ #t)
+ ((null? sbuf-elm-list)
+ #f)
+ ((equal? (car elm-list) (car sbuf-elm-list))
+ (loop (cdr elm-list) (cdr sbuf-elm-list)))
+ (else
+ (loop elm-list (cdr sbuf-elm-list)))))
+)
+
+; Subroutine of compute-sformat-bufs!.
+; Lookup ELM-LIST in SBUF-LIST. A match is found if ELM-LIST
+; is a subset of one in SBUF-LIST.
+; Return the containing <sformat-argbuf> object if found, otherwise return #f.
+; SBUF-LIST is a list of <sformat-argbuf> objects.
+; ELM-LIST is (elm1 elm2 ...).
+
+(define (-sbuf-lookup elm-list sbuf-list)
+ (let loop ((sbuf-list sbuf-list))
+ (cond ((null? sbuf-list)
+ #f)
+ ((-sbuf-subset? elm-list (car sbuf-list))
+ (car sbuf-list))
+ (else
+ (loop (cdr sbuf-list)))))
+)
+
+; Compute and record the set of <sformat-argbuf> objects needed for SFMT-LIST,
+; a list of all sformats.
+; The result is the computed list of <sformat-argbuf> objects.
+;
+; This is used to further reduce the number of entries in the argument buffer's
+; `fields' union. Some sformats have structs with the same contents or one is
+; a subset of another's, thus there is no need to distinguish them as far as
+; the struct is concerned (there may be other reasons to distinguish them of
+; course).
+; The consequence of this is fewer semantic fragments created in with-sem-frags
+; pbb engines.
+
+(define (compute-sformat-argbufs! sfmt-list)
+ (logit 1 "Computing sformat argument buffers ...\n")
+
+ (let ((sfmt-contents
+ ; Sort by descending length. This helps building the result: while
+ ; iterating over each element, its sbuf is either a subset of a
+ ; previous entry or requires a new entry.
+ (sort (map -sfmt-contents sfmt-list)
+ (lambda (a b)
+ (> (length a) (length b)))))
+ ; Build an <sformat-argbuf> object.
+ (build-sbuf (lambda (sfmt-data)
+ (make <sformat-argbuf>
+ (obj:name (car sfmt-data))
+ ""
+ atlist-empty
+ (cdr sfmt-data))))
+ )
+ ; Start off with the first sfmt.
+ ; Also build an empty sbuf. Which sbuf to use for an empty argument list
+ ; is rather arbitrary. Rather than pick one, keep the empty sbuf unto
+ ; itself.
+ (let ((nub-sbufs (list (build-sbuf (car sfmt-contents))))
+ (empty-sbuf (make <sformat-argbuf>
+ 'fmt-empty "no operands" atlist-empty
+ nil))
+ )
+ (sfmt-set-sbuf! (caar sfmt-contents) (car nub-sbufs))
+
+ ; Now loop over the remaining sfmts.
+ (let loop ((sfmt-contents (cdr sfmt-contents)))
+ (if (not (null? sfmt-contents))
+ (let ((sfmt-data (car sfmt-contents)))
+ (if (null? (cdr sfmt-data))
+ (sfmt-set-sbuf! (car sfmt-data) empty-sbuf)
+ (let ((sbuf (-sbuf-lookup (cdr sfmt-data) nub-sbufs)))
+ (if (not sbuf)
+ (begin
+ (set! sbuf (build-sbuf sfmt-data))
+ (set! nub-sbufs (cons sbuf nub-sbufs))))
+ (sfmt-set-sbuf! (car sfmt-data) sbuf)))
+ (loop (cdr sfmt-contents)))))
+
+ ; Done.
+ ; Note that the result will be sorted by ascending number of elements
+ ; (because the search list was sorted by descending length and the result
+ ; is built up in reverse order of that).
+ ; Not that it matters, but that's kinda nice.
+ (cons empty-sbuf nub-sbufs)))
+)
+
+; Profiling support.
+
+; By default hardware elements are not profilable.
+
+(method-make! <hardware-base> 'profilable? (lambda (self) #f))
+
+(method-make!
+ <hw-register> 'profilable?
+ (lambda (self) (has-attr? self 'PROFILE))
+)
+
+; Return boolean indicating if HW is profilable.
+
+(define (hw-profilable? hw) (send hw 'profilable?))
+
+; Return a boolean indicating if OP is profilable.
+
+(define (op-profilable? op)
+ (hw-profilable? (op:type op))
+)
+
+; sbuf-profile-data method.
+; Return a list of C type and size to use in an sformat's argument buffer.
+
+(method-make!
+ <hardware-base> 'sbuf-profile-data
+ (lambda (self)
+ (error "sbuf-profile-elm not supported for this hw type"))
+)
+
+(method-make!
+ <hw-register> 'sbuf-profile-data
+ (lambda (self)
+ ; Don't unnecessarily bloat size of argument buffer.
+ (if (<= (hw-num-elms self) 255)
+ (list "unsigned char" 8)
+ (list "unsigned short" 16)))
+)
+
+; sbuf-profile-elm method.
+; Return the ARGBUF member needed for profiling SELF in <sformat> SFMT.
+; The result is (var-name "C-type" approx-bitsize) or #f if unneeded.
+
+(method-make!
+ <operand> 'sbuf-profile-elm
+ (lambda (self sfmt out?)
+ (if (hw-scalar? (op:type self))
+ #f
+ (cons (string-append (if out? "out_" "in_")
+ (gen-sym self))
+ (send (op:type self) 'sbuf-profile-data))))
+)
+
+; Subroutine of -sfmt-contents to return an operand's profile element.
+; The result is (var-name "C-type" approx-bitsize) or #f if unneeded.
+
+(define (sfmt-op-profile-elm op sfmt out?)
+ (send op 'sbuf-profile-elm sfmt out?)
+)
+
+; ARGBUF accessor support.
+
+; Define and undefine C macros to tuck away details of instruction format used
+; in the extraction and semantic code. Instruction format names can
+; change frequently and this can result in unnecessarily large diffs from one
+; generated version of the file to the next. Secondly, tucking away details of
+; the extracted argument structure from the extraction code is a good thing.
+
+; Name of macro to access fields in ARGBUF.
+(define c-argbuf-macro "FLD")
+
+(define (gen-define-argbuf-macro sfmt)
+ (string-append "#define " c-argbuf-macro "(f) "
+ "abuf->fields."
+ (gen-sym (sfmt-sbuf sfmt))
+ ".f\n")
+)
+
+(define (gen-undef-argbuf-macro sfmt)
+ (string-append "#undef " c-argbuf-macro "\n")
+)
+
+; For old code. Delete in time.
+(define gen-define-field-macro gen-define-argbuf-macro)
+(define gen-undef-field-macro gen-undef-argbuf-macro)
+
+; Return a C reference to an ARGBUF field value.
+
+(define (gen-argbuf-ref name)
+ (string-append c-argbuf-macro " (" name ")")
+)
+
+; Return name of ARGBUF member for extracted <field> F.
+
+(define (gen-ifld-argbuf-name f)
+ (gen-sym f)
+)
+
+; Return the C reference to a cached ifield.
+
+(define (gen-ifld-argbuf-ref f)
+ (gen-argbuf-ref (gen-ifld-argbuf-name f))
+)
+
+; Return name of ARGBUF member holding processed from of extracted
+; ifield value for <hw-index> index.
+
+(define (gen-hw-index-argbuf-name index)
+ (gen-sym index)
+)
+
+; Return C reference to a processed <hw-index> in ARGBUF.
+
+(define (gen-hw-index-argbuf-ref index)
+ (gen-argbuf-ref (gen-hw-index-argbuf-name index))
+)
+
+; Decode support.
+
+; Main procedure call tree:
+; cgen-decode.{c,cxx}
+; -gen-decode-fn
+; gen-decoder [our entry point]
+; decode-build-table
+; -gen-decoder-switch
+; -gen-decoder-switch
+;
+; decode-build-table is called to construct a tree of "table-guts" elements
+; (??? Need better name obviously),
+; and then gen-decoder is recursively called on each of these elements.
+
+; Return C/C++ code that fetches the desired decode bits from C value VAL.
+; SIZE is the size in bits of val (the MSB is 1 << (size - 1)) which we
+; treat as bitnum 0.
+; BITNUMS must be monotonically increasing.
+; LSB0? is non-#f if bit number 0 is the least significant bit.
+; FIXME: START may not be handled right in words beyond first.
+;
+; e.g. (-gen-decode-bits '(0 1 2 3 8 9 10 11) 0 16 "insn" #f)
+; --> "(((insn >> 8) & 0xf0) | ((insn >> 4) & 0xf))"
+; FIXME: The generated code has some inefficiencies in edge cases. Later.
+
+(define (-gen-decode-bits bitnums start size val lsb0?)
+
+ ; Compute a list of lists of three numbers:
+ ; (first bitnum in group, position in result (0=LSB), bits in result)
+
+ (let ((groups
+ ; POS = starting bit position of current group.
+ ; COUNT = number of bits in group.
+ ; Work from least to most significant bit so reverse bitnums.
+ (let loop ((result nil) (pos 0) (count 0) (bitnums (reverse bitnums)))
+ ;(display (list result pos count bitnums)) (newline)
+ (if (null? bitnums)
+ result
+ (if (or (= (length bitnums) 1)
+ ; Are numbers not next to each other?
+ (not (= (- (car bitnums) (if lsb0? -1 1))
+ (cadr bitnums))))
+ (loop (cons (list (car bitnums) pos (+ 1 count))
+ result)
+ (+ pos count 1) 0
+ (cdr bitnums))
+ (loop result
+ pos (+ 1 count)
+ (cdr bitnums)))))))
+ (string-append
+ "("
+ (string-drop 3
+ (string-map
+ (lambda (group)
+ (let* ((first (car group))
+ (pos (cadr group))
+ (bits (caddr group))
+ ; Difference between where value is and where
+ ; it needs to be.
+ ; FIXME: Need to handle left (-ve) shift.
+ (shift (- (if lsb0?
+ (- first bits -1)
+ (- (+ start size) (+ first bits)))
+ pos)))
+ (string-append
+ " | ((" val " >> " (number->string shift)
+ ") & ("
+ (number->string (- (integer-expt 2 bits) 1))
+ " << " (number->string pos) "))")))
+ groups))
+ ")"))
+)
+
+; Convert decoder table into C code.
+
+; Return code for one insn entry.
+; REST is the remaining entries.
+
+(define (-gen-decode-insn-entry entry rest indent)
+ (assert (eq? 'insn (dtable-entry-type entry)))
+ (logit 3 "Generating decode insn entry for " (obj:name (dtable-entry-value entry)) " ...\n")
+
+ (let ((insn (dtable-entry-value entry)))
+
+ (cond
+
+ ; Leave invalids to the default case.
+ ((eq? (obj:name insn) 'x-invalid)
+ "")
+
+ ; If same contents as next case, fall through.
+ ; FIXME: Can reduce more by sorting cases. Much later.
+ ((and (not (null? rest))
+ ; Ensure both insns.
+ (eq? 'insn (dtable-entry-type (car rest)))
+ ; Ensure same insn.
+ (eq? (obj:name insn)
+ (obj:name (dtable-entry-value (car rest)))))
+ (string-append indent " case "
+ (number->string (dtable-entry-index entry))
+ " : /* fall through */\n"))
+
+ (else
+ (string-append indent " case "
+ (number->string (dtable-entry-index entry))
+ " : itype = "
+ (gen-cpu-insn-enum (current-cpu) insn)
+ "; "
+ (if (with-scache?)
+ (string-append "goto "
+ "extract_"
+ (gen-sym (insn-sfmt insn))
+ ";\n")
+ "goto done;\n")))))
+)
+
+; Subroutine of -decode-expr-ifield-tracking.
+; Return a list of all possible values for ifield IFLD-NAME.
+; FIXME: Quick-n-dirty implementation. Should use bit arrays.
+
+(define (-decode-expr-ifield-values ifld-name)
+ (let* ((ifld (current-ifld-lookup ifld-name))
+ (bits (ifld-length ifld)))
+ (if (mode-unsigned? (ifld-mode ifld))
+ (iota (logsll 1 bits))
+ (iota (- (logsll 1 (- bits 1))) (logsll 1 bits))))
+)
+
+; Subroutine of -decode-expr-ifield-tracking,-decode-expr-ifield-mark-used.
+; Create the search key for tracking table lookup.
+
+(define (-decode-expr-ifield-tracking-key insn ifld-name)
+ (symbol-append (obj:name (insn-ifmt insn)) '-x- ifld-name)
+)
+
+; Subroutine of -gen-decode-expr-entry.
+; Return a table to track used ifield values.
+; The table is an associative list of (key . value-list).
+; KEY is "iformat-name-x-ifield-name".
+; VALUE-LIST is a list of the unused values.
+
+(define (-decode-expr-ifield-tracking expr-list)
+ (let ((table1
+ (apply append
+ (map (lambda (entry)
+ (map (lambda (ifld-name)
+ (cons (exprtable-entry-insn entry)
+ (cons ifld-name
+ (-decode-expr-ifield-values ifld-name))))
+ (exprtable-entry-iflds entry)))
+ expr-list))))
+ ; TABLE1 is a list of (insn ifld-name value1 value2 ...).
+ (nub (map (lambda (elm)
+ (cons
+ (-decode-expr-ifield-tracking-key (car elm) (cadr elm))
+ (cddr elm)))
+ table1)
+ car))
+)
+
+; Subroutine of -decode-expr-ifield-mark-used!.
+; Return list of values completely used for ifield IFLD-NAME in EXPR.
+; "completely used" here means the value won't appear elsewhere.
+; e.g. in (andif (eq f-rd 15) (eq f-rx 14)) we don't know what happens
+; for the (ne f-rx 14) case.
+
+(define (-decode-expr-ifield-values-used ifld-name expr)
+ (case (rtx-name expr)
+ ((eq)
+ (if (and (rtx-kind? 'ifield (rtx-cmp-op-arg expr 0))
+ (rtx-constant? (rtx-cmp-op-arg expr 1)))
+ (list (rtx-constant-value (rtx-cmp-op-arg expr 1)))
+ nil))
+ ((member)
+ (if (rtx-kind? 'ifield (rtx-member-value expr))
+ (rtx-member-set expr)
+ nil))
+ ; FIXME: more needed
+ (else nil))
+)
+
+; Subroutine of -gen-decode-expr-entry.
+; Mark ifield values used by EXPR-ENTRY in TRACKING-TABLE.
+
+(define (-decode-expr-ifield-mark-used! tracking-table expr-entry)
+ (let ((insn (exprtable-entry-insn expr-entry))
+ (expr (exprtable-entry-expr expr-entry))
+ (ifld-names (exprtable-entry-iflds expr-entry)))
+ (for-each (lambda (ifld-name)
+ (let ((table-entry
+ (assq (-decode-expr-ifield-tracking-key insn ifld-name)
+ tracking-table))
+ (used (-decode-expr-ifield-values-used ifld-name expr)))
+ (for-each (lambda (value)
+ (delq! value table-entry))
+ used)
+ ))
+ ifld-names))
+ *UNSPECIFIED*
+)
+
+; Subroutine of -gen-decode-expr-entry.
+; Return code to set `itype' and branch to the extraction phase.
+
+(define (-gen-decode-expr-set-itype indent insn-enum fmt-name)
+ (string-append
+ indent
+ "{ itype = "
+ insn-enum
+ "; "
+ (if (with-scache?)
+ (string-append "goto "
+ "extract_"
+ fmt-name
+ ";")
+ "goto done;")
+ " }\n"
+ )
+)
+
+; Generate code to decode the expression table in ENTRY.
+; INVALID-INSN is the <insn> object of the pseudo insn to handle invalid ones.
+
+(define (-gen-decode-expr-entry entry indent invalid-insn)
+ (assert (eq? 'expr (dtable-entry-type entry)))
+ (logit 3 "Generating decode expr entry for " (exprtable-name (dtable-entry-value entry)) " ...\n")
+
+ (let ((expr-list (exprtable-insns (dtable-entry-value entry))))
+ (string-list
+ indent " case "
+ (number->string (dtable-entry-index entry))
+ " :\n"
+
+ (let ((iflds-tracking (-decode-expr-ifield-tracking expr-list))
+ (indent (string-append indent " ")))
+
+ (let loop ((expr-list expr-list) (code nil))
+
+ (if (null? expr-list)
+
+ ; All done. If we used up all field values we don't need to
+ ; "fall through" and select the invalid insn marker.
+
+ (if (all-true? (map null? (map cdr iflds-tracking)))
+ code
+ (append! code
+ (list
+ (-gen-decode-expr-set-itype
+ indent
+ (gen-cpu-insn-enum (current-cpu) invalid-insn)
+ "sfmt_empty"))))
+
+ ; Not all done, process next expr.
+
+ (let ((insn (exprtable-entry-insn (car expr-list)))
+ (expr (exprtable-entry-expr (car expr-list)))
+ (ifld-names (exprtable-entry-iflds (car expr-list))))
+
+ ; Mark of those ifield values we use first.
+ ; If there are none left afterwards, we can unconditionally
+ ; choose this insn.
+ (-decode-expr-ifield-mark-used! iflds-tracking (car expr-list))
+
+ (let ((next-code
+ ; If this is the last expression, and it uses up all
+ ; remaining ifield values, there's no need to perform any
+ ; test.
+ (if (and (null? (cdr expr-list))
+ (all-true? (map null? (map cdr iflds-tracking))))
+
+ ; Need this in a list for a later append!.
+ (string-list
+ (-gen-decode-expr-set-itype
+ indent
+ (gen-cpu-insn-enum (current-cpu) insn)
+ (gen-sym (insn-sfmt insn))))
+
+ ; We don't use up all ifield values, so emit a test.
+ (let ((iflds (map current-ifld-lookup ifld-names)))
+ (string-list
+ indent "{\n"
+ (gen-define-ifields iflds
+ (insn-length insn)
+ (string-append indent " ")
+ #f)
+ (gen-extract-ifields iflds
+ (insn-length insn)
+ (string-append indent " ")
+ #f)
+ indent " if ("
+ (rtl-c 'BI expr nil #:ifield-var? #t)
+ ")\n"
+ (-gen-decode-expr-set-itype
+ (string-append indent " ")
+ (gen-cpu-insn-enum (current-cpu) insn)
+ (gen-sym (insn-sfmt insn)))
+ indent "}\n")))))
+
+ (loop (cdr expr-list)
+ (append! code next-code)))))))
+ ))
+)
+
+; Generate code to decode TABLE.
+; REST is the remaining entries.
+; SWITCH-NUM, STARTBIT, DECODE-BITSIZE, INDENT, LSB0?, INVALID-INSN are same
+; as for -gen-decoder-switch.
+
+(define (-gen-decode-table-entry table rest switch-num startbit decode-bitsize indent lsb0? invalid-insn)
+ (assert (eq? 'table (dtable-entry-type table)))
+ (logit 3 "Generating decode table entry for case " (dtable-entry-index table) " ...\n")
+
+ (string-list
+ indent " case "
+ (number->string (dtable-entry-index table))
+ " :"
+ ; If table is same as next, just emit a "fall through" to cut down on
+ ; generated code.
+ (if (and (not (null? rest))
+ ; Ensure both tables.
+ (eq? 'table (dtable-entry-type (car rest)))
+ ; Ensure same table.
+ (eqv? (subdtable-key (dtable-entry-value table))
+ (subdtable-key (dtable-entry-value (car rest)))))
+ " /* fall through */\n"
+ (string-list
+ "\n"
+ (-gen-decoder-switch switch-num
+ startbit
+ decode-bitsize
+ (subdtable-table (dtable-entry-value table))
+ (string-append indent " ")
+ lsb0?
+ invalid-insn))))
+)
+
+; Subroutine of -decode-sort-entries.
+; Return a boolean indicating if A,B are equivalent entries.
+
+(define (-decode-equiv-entries? a b)
+ (let ((a-type (dtable-entry-type a))
+ (b-type (dtable-entry-type b)))
+ (if (eq? a-type b-type)
+ (case a-type
+ ((insn)
+ (let ((a-name (obj:name (dtable-entry-value a)))
+ (b-name (obj:name (dtable-entry-value b))))
+ (eq? a-name b-name)))
+ ((expr)
+ ; Ignore expr entries for now.
+ #f)
+ ((table)
+ (let ((a-name (subdtable-key (dtable-entry-value a)))
+ (b-name (subdtable-key (dtable-entry-value b))))
+ (eq? a-name b-name))))
+ ; A and B are not the same type.
+ #f))
+)
+
+; Subroutine of -gen-decoder-switch, sort ENTRIES according to desired
+; print order (maximizes amount of fall-throughs, but maintains numerical
+; order as much as possible).
+; ??? This is an O(n^2) algorithm. An O(n Log(n)) algorithm can be done
+; but it seemed more complicated than necessary for now.
+
+(define (-decode-sort-entries entries)
+ (let ((find-equiv!
+ ; Return list of entries in non-empty list L that have the same decode
+ ; entry as the first entry. Entries found are marked with #f so
+ ; they're not processed again.
+ (lambda (l)
+ ; Start off the result with the first entry, then see if the
+ ; remaining ones match it.
+ (let ((first (car l)))
+ (let loop ((l (cdr l)) (result (cons first nil)))
+ (if (null? l)
+ (reverse! result)
+ (if (and (car l) (-decode-equiv-entries? first (car l)))
+ (let ((lval (car l)))
+ (set-car! l #f)
+ (loop (cdr l) (cons lval result)))
+ (loop (cdr l) result)))))))
+ )
+ (let loop ((entries (list-copy entries)) (result nil))
+ (if (null? entries)
+ (apply append (reverse! result))
+ (if (car entries)
+ (loop (cdr entries)
+ (cons (find-equiv! entries)
+ result))
+ (loop (cdr entries) result)))))
+)
+
+; Generate switch statement to decode TABLE-GUTS.
+; SWITCH-NUM is for compatibility with the computed goto decoder and
+; isn't used.
+; STARTBIT is the bit offset of the instruction value that C variable `insn'
+; holds (note that this is independent of LSB0?).
+; DECODE-BITSIZE is the number of bits of the insn that `insn' holds.
+; LSB0? is non-#f if bit number 0 is the least significant bit.
+; INVALID-INSN is the <insn> object of the pseudo insn to handle invalid ones.
+
+(define (-gen-decoder-switch switch-num startbit decode-bitsize table-guts indent lsb0? invalid-insn)
+ ; For entries that are a single insn, we're done, otherwise recurse.
+
+ (string-list
+ indent "{\n"
+ ; Are we at the next word?
+ (if (not (= startbit (dtable-guts-startbit table-guts)))
+ (begin
+ (set! startbit (dtable-guts-startbit table-guts))
+ (set! decode-bitsize (dtable-guts-bitsize table-guts))
+ ; FIXME: Bits may get fetched again during extraction.
+ (string-append indent " unsigned int val;\n"
+ indent " /* Must fetch more bits. */\n"
+ indent " insn = "
+ (gen-ifetch "pc" startbit decode-bitsize)
+ ";\n"
+ indent " val = "))
+ (string-append indent " unsigned int val = "))
+ (-gen-decode-bits (dtable-guts-bitnums table-guts)
+ (dtable-guts-startbit table-guts)
+ (dtable-guts-bitsize table-guts) "insn" lsb0?)
+ ";\n"
+ indent " switch (val)\n"
+ indent " {\n"
+
+ ; The code is more readable, and icache use is improved, if we collapse
+ ; common code into one case and use "fall throughs" for all but the last of
+ ; a set of common cases.
+ ; FIXME: We currently rely on -gen-decode-foo-entry to recognize the fall
+ ; through. We should take care of it ourselves.
+
+ (let loop ((entries (-decode-sort-entries (dtable-guts-entries table-guts)))
+ (result nil))
+ (if (null? entries)
+ (reverse! result)
+ (loop
+ (cdr entries)
+ (cons (case (dtable-entry-type (car entries))
+ ((insn)
+ (-gen-decode-insn-entry (car entries) (cdr entries) indent))
+ ((expr)
+ (-gen-decode-expr-entry (car entries) indent invalid-insn))
+ ((table)
+ (-gen-decode-table-entry (car entries) (cdr entries)
+ switch-num startbit decode-bitsize
+ indent lsb0? invalid-insn))
+ )
+ result))))
+
+ ; ??? Can delete if all cases are present.
+ indent " default : itype = "
+ (gen-cpu-insn-enum (current-cpu) invalid-insn)
+ ";"
+ (if (with-scache?)
+ " goto extract_sfmt_empty;\n"
+ " goto done;\n")
+ indent " }\n"
+ indent "}\n"
+ )
+)
+
+; Decoder generation entry point.
+; Generate code to decode INSN-LIST.
+; BITNUMS is the set of bits to initially key off of.
+; DECODE-BITSIZE is the number of bits of the instruction that `insn' holds.
+; LSB0? is non-#f if bit number 0 is the least significant bit.
+; INVALID-INSN is the <insn> object of the pseudo insn to handle invalid ones.
+
+(define (gen-decoder insn-list bitnums decode-bitsize indent lsb0? invalid-insn)
+ (logit 3 "Building decode tree.\n"
+ "bitnums = " (stringize bitnums " ") "\n"
+ "decode-bitsize = " (number->string decode-bitsize) "\n"
+ "lsb0? = " (if lsb0? "#t" "#f") "\n"
+ )
+
+ ; First build a table that decodes the instruction set.
+
+ (let ((table-guts (decode-build-table insn-list bitnums
+ decode-bitsize lsb0?
+ invalid-insn)))
+
+ ; Now print it out.
+
+ (-gen-decoder-switch "0" 0 decode-bitsize table-guts indent lsb0?
+ invalid-insn)
+ )
+)