; CPU architecture description. ; Copyright (C) 2000 Red Hat, Inc. ; This file is part of CGEN. ; See file COPYING.CGEN for details. ; Top level class that records everything about a cpu. ; FIXME: Rename this to something else and rename to ; for consistency with other classes (define-foo -> object). (define (class-make ' nil '( ; An object of type . data (attr-list . (() . ())) (enum-list . ()) (kw-list . ()) (isa-list . ()) (cpu-list . ()) (mach-list . ()) (model-list . ()) (ifld-list . ()) (hw-list . ()) (op-list . ()) (ifmt-list . ()) (sfmt-list . ()) (insn-list . ()) (minsn-list . ()) (subr-list . ()) (insn-extract . #f) ; FIXME: wip (and move elsewhere) (insn-execute . #f) ; FIXME: wip (and move elsewhere) ; standard values derived from the input data derived ; #t if instructions have been analyzed (insns-analyzed? . #f) ; #t if semantics were included in the analysis (semantics-analyzed? . #f) ; #t if alias insns were included in the analysis (aliases-analyzed? . #f) ) nil) ) ; Accessors. ; Each getter is arch-foo. ; Each setter is arch-set-foo!. (define-getters arch (data attr-list enum-list kw-list isa-list cpu-list mach-list model-list ifld-list hw-list op-list ifmt-list sfmt-list insn-list minsn-list subr-list derived insns-analyzed? semantics-analyzed? aliases-analyzed? ) ) (define-setters arch (data attr-list enum-list kw-list isa-list cpu-list mach-list model-list ifld-list hw-list op-list ifmt-list sfmt-list insn-list minsn-list subr-list derived insns-analyzed? semantics-analyzed? aliases-analyzed? ) ) ; Class for recording things specified in `define-arch'. ; This simplifies define-arch as the global arch object CURRENT-ARCH ; must exist before loading the .cpu file. (define (class-make ' '() '( ; Default alignment of memory operations. ; One of aligned, unaligned, forced. default-alignment ; Orientation of insn bit numbering (#f->msb=0, #t->lsb=0). insn-lsb0? ; List of all machs. ; Each element is pair of (mach-name . sanitize-key) ; where sanitize-key is #f if there is none. ; blah blah blah ... ooohhh, evil sanitize key, blah blah blah machs ; List of all isas (instruction set architecture). ; Each element is a pair of (isa-name . sanitize-key) ; where sanitize-key is #f if there is none. ; There is usually just one. ARM has two (arm, thumb). ; blah blah blah ... ooohhh, evil sanitize key, blah blah blah isas ; ??? Defaults for other things should be here. ) nil) ) (define-getters adata (default-alignment insn-lsb0? machs isas) ) ; Add, list, lookup accessors for . ; ; For the lookup routines, the result is the object or #f if not found. ; For some, if X is already an object, return that. (define (current-arch-name) (obj:name (arch-data CURRENT-ARCH))) (define (current-arch-comment) (obj:comment (arch-data CURRENT-ARCH))) (define (current-arch-atlist) (obj-atlist (arch-data CURRENT-ARCH))) (define (current-arch-default-alignment) (adata-default-alignment (arch-data CURRENT-ARCH))) (define (current-arch-insn-lsb0?) (adata-insn-lsb0? (arch-data CURRENT-ARCH))) (define (current-arch-mach-name-list) (map car (adata-machs (arch-data CURRENT-ARCH))) ) (define (current-arch-isa-name-list) (map car (adata-isas (arch-data CURRENT-ARCH))) ) ; Attributes. ; Recorded as a pair of lists. ; The car is a list of objects. ; The cdr is an associative list of (name . ) elements, for lookup. ; Could use a hash table except that there currently aren't that many. (define (current-attr-list) (car (arch-attr-list CURRENT-ARCH))) (define (current-attr-add! a) ; NOTE: While putting this test in define-attr feels better, having it here ; is more robust, internal calls get checked too. Thus it's here. ; Ditto for all the other such tests in this file. (if (current-attr-lookup (obj:name a)) (parse-error "define-attr" "attribute already defined" (obj:name a))) (let ((adata (arch-attr-list CURRENT-ARCH))) ; Build list in normal order so we don't have to reverse it at the end ; (since our format is non-trivial). (if (null? (car adata)) (arch-set-attr-list! CURRENT-ARCH (cons (cons a nil) (acons (obj:name a) a nil))) (begin (append! (car adata) (cons a nil)) (append! (cdr adata) (acons (obj:name a) a nil))))) *UNSPECIFIED* ) (define (current-attr-lookup attr-name) (assq-ref (cdr (arch-attr-list CURRENT-ARCH)) attr-name) ) ; Enums. (define (current-enum-list) (arch-enum-list CURRENT-ARCH)) (define (current-enum-add! e) (if (current-enum-lookup (obj:name e)) (parse-error "define-enum" "enum already defined" (obj:name e))) (arch-set-enum-list! CURRENT-ARCH (cons e (arch-enum-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-enum-lookup enum-name) (object-assq enum-name (current-enum-list)) ) ; Keywords. (define (current-kw-list) (arch-kw-list CURRENT-ARCH)) (define (current-kw-add! kw) (if (current-kw-lookup (obj:name kw)) (parse-error "define-keyword" "keyword already defined" (obj:name kw))) (arch-set-kw-list! CURRENT-ARCH (cons kw (arch-kw-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-kw-lookup kw-name) (object-assq kw-name (current-kw-list)) ) ; Instruction sets. (define (current-isa-list) (arch-isa-list CURRENT-ARCH)) (define (current-isa-add! i) (if (current-isa-lookup (obj:name i)) (parse-error "define-isa" "isa already defined" (obj:name i))) (arch-set-isa-list! CURRENT-ARCH (cons i (arch-isa-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-isa-lookup isa-name) (object-assq isa-name (current-isa-list)) ) ; Cpu families. (define (current-cpu-list) (arch-cpu-list CURRENT-ARCH)) (define (current-cpu-add! c) (if (current-cpu-lookup (obj:name c)) (parse-error "define-cpu" "cpu already defined" (obj:name c))) (arch-set-cpu-list! CURRENT-ARCH (cons c (arch-cpu-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-cpu-lookup cpu-name) (object-assq cpu-name (current-cpu-list)) ) ; Machines. (define (current-mach-list) (arch-mach-list CURRENT-ARCH)) (define (current-mach-add! m) (if (current-mach-lookup (obj:name m)) (parse-error "define-mach" "mach already defined" (obj:name m))) (arch-set-mach-list! CURRENT-ARCH (cons m (arch-mach-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-mach-lookup mach-name) (object-assq mach-name (current-mach-list)) ) ; Models. (define (current-model-list) (arch-model-list CURRENT-ARCH)) (define (current-model-add! m) (if (current-model-lookup (obj:name m)) (parse-error "define-model" "model already defined" (obj:name m))) (arch-set-model-list! CURRENT-ARCH (cons m (arch-model-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-model-lookup model-name) (object-assq model-name (current-model-list)) ) ; Hardware elements. (define (current-hw-list) (arch-hw-list CURRENT-ARCH)) (define (current-hw-add! hw) (if (current-hw-lookup (obj:name hw)) (parse-error "define-hardware" "hardware already defined" (obj:name hw))) (arch-set-hw-list! CURRENT-ARCH (cons hw (arch-hw-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-hw-lookup hw) (if (object? hw) hw ; This doesn't use object-assq on purpose. Hardware objects handle ; get-name specially. (find-first (lambda (hw-obj) (eq? (send hw-obj 'get-name) hw)) (current-hw-list))) ) ; Instruction fields. (define (current-ifld-list) (map cdr (arch-ifld-list CURRENT-ARCH))) (define (current-ifld-add! f) (if (-ifld-already-defined? f) (parse-error "define-ifield" "ifield already defined" (obj:name f))) (arch-set-ifld-list! CURRENT-ARCH (acons (obj:name f) f (arch-ifld-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-ifld-lookup x) (if (ifield? x) x (assq-ref (arch-ifld-list CURRENT-ARCH) x)) ) ; Return a boolean indicating if F is currently defined. ; This is slightly complicated because multiple isas can have different ; ifields with the same name. (define (-ifld-already-defined? f) (let ((iflds (find (lambda (ff) (eq? (obj:name f) (car ff))) (arch-ifld-list CURRENT-ARCH)))) ; We've got all the ifields with the same name, ; now see if any have the same ISA as F. (let ((result #f) (f-isas (obj-isa-list f))) (for-each (lambda (ff) (if (not (null? (intersection f-isas (obj-isa-list (cdr ff))))) (set! result #t))) iflds) result)) ) ; Operands. (define (current-op-list) (map cdr (arch-op-list CURRENT-ARCH))) (define (current-op-add! op) (if (-op-already-defined? op) (parse-error "define-operand" "operand already defined" (obj:name op))) (arch-set-op-list! CURRENT-ARCH (acons (obj:name op) op (arch-op-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-op-lookup name) (assq-ref (arch-op-list CURRENT-ARCH) name) ) ; Return a boolean indicating if OP is currently defined. ; This is slightly complicated because multiple isas can have different ; operands with the same name. (define (-op-already-defined? op) (let ((ops (find (lambda (o) (eq? (obj:name op) (car o))) (arch-op-list CURRENT-ARCH)))) ; We've got all the operands with the same name, ; now see if any have the same ISA as OP. (let ((result #f) (op-isas (obj-isa-list op))) (for-each (lambda (o) (if (not (null? (intersection op-isas (obj-isa-list (cdr o))))) (set! result #t))) ops) result)) ) ; Instruction field formats. (define (current-ifmt-list) (arch-ifmt-list CURRENT-ARCH)) ; Semantic formats (akin to ifmt's, except includes semantics to distinguish ; insns). (define (current-sfmt-list) (arch-sfmt-list CURRENT-ARCH)) ; Instructions. (define (current-raw-insn-list) (arch-insn-list CURRENT-ARCH)) (define (current-insn-list) (map cdr (arch-insn-list CURRENT-ARCH))) (define (current-insn-add! i) (if (-insn-already-defined? i) (parse-error "define-insn" "insn already defined" (obj:name i))) (arch-set-insn-list! CURRENT-ARCH (acons (obj:name i) i (arch-insn-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-insn-lookup name) (assq-ref (arch-insn-list CURRENT-ARCH) name) ) ; Return a boolean indicating if INSN is currently defined. ; This is slightly complicated because multiple isas can have different ; insns with the same name. (define (-insn-already-defined? insn) (let ((insns (find (lambda (i) (eq? (obj:name insn) (car i))) (arch-insn-list CURRENT-ARCH)))) ; We've got all the insns with the same name, ; now see if any have the same ISA as INSN. (let ((result #f) (insn-isas (obj-isa-list insn))) (for-each (lambda (i) (if (not (null? (intersection insn-isas (obj-isa-list (cdr i))))) (set! result #t))) insns) result)) ) ; Return the insn in the `car' position of INSN-LIST. (define insn-list-car cdar) ; Splice INSN into INSN-LIST after (car INSN-LIST). ; This is useful when creating machine generating insns - it's useful to ; keep them close to their progenitor. ; The result is the same list, but beginning at the spliced-in insn. (define (insn-list-splice! insn-list insn) (set-cdr! insn-list (acons (obj:name insn) insn (cdr insn-list))) (cdr insn-list) ) ; Macro instructions. (define (current-minsn-list) (map cdr (arch-minsn-list CURRENT-ARCH))) (define (current-minsn-add! m) (if (-minsn-already-defined? m) (parse-error "define-minsn" "macro-insn already defined" (obj:name m))) (arch-set-minsn-list! CURRENT-ARCH (acons (obj:name m) m (arch-minsn-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-minsn-lookup name) (assq-ref (arch-minsn-list CURRENT-ARCH) name) ) ; Return a boolean indicating if MINSN is currently defined. ; This is slightly complicated because multiple isas can have different ; macro-insns with the same name. (define (-minsn-already-defined? m) (let ((minsns (find (lambda (mm) (eq? (obj:name m) (car mm))) (arch-minsn-list CURRENT-ARCH)))) ; We've got all the macro-insns with the same name, ; now see if any have the same ISA as M. (let ((result #f) (m-isas (obj-isa-list m))) (for-each (lambda (mm) (if (not (null? (intersection m-isas (obj-isa-list (cdr mm))))) (set! result #t))) minsns) result)) ) ; rtx subroutines. (define (current-subr-list) (map cdr (arch-subr-list CURRENT-ARCH))) (define (current-subr-add! s) (if (current-subr-lookup (obj:name s)) (parse-error "define-subr" "subroutine already defined" (obj:name s))) (arch-set-subr-list! CURRENT-ARCH (acons (obj:name s) s (arch-subr-list CURRENT-ARCH))) *UNSPECIFIED* ) (define (current-subr-lookup name) (assq-ref (arch-subr-list CURRENT-ARCH) name) ) ; Arch parsing support. ; Parse an alignment spec. (define (-arch-parse-alignment errtxt alignment) (if (memq alignment '(aligned unaligned forced)) alignment (parse-error errtxt "invalid alignment" alignment)) ) ; Parse an arch mach spec. ; The value is a list of mach names or (mach-name sanitize-key) elements. ; The result is a list of (mach-name . sanitize-key) elements. (define (-arch-parse-machs errtxt machs) (for-each (lambda (m) (if (or (symbol? m) (and (list? m) (= (length m) 2) (symbol? (car m)) (symbol? (cadr m)))) #t ; ok (parse-error errtxt "bad arch mach spec" m))) machs) (map (lambda (m) (if (symbol? m) (cons m #f) (cons (car m) (cadr m)))) machs) ) ; Parse an arch isa spec. ; The value is a list of isa names or (isa-name sanitize-key) elements. ; The result is a list of (isa-name . sanitize-key) elements. (define (-arch-parse-isas errtxt isas) (for-each (lambda (m) (if (or (symbol? m) (and (list? m) (= (length m) 2) (symbol? (car m)) (symbol? (cadr m)))) #t ; ok (parse-error errtxt "bad arch isa spec" m))) isas) (map (lambda (m) (if (symbol? m) (cons m #f) (cons (car m) (cadr m)))) isas) ) ; Parse an architecture description ; This is the main routine for building an arch object from a cpu ; description in the .cpu file. ; All arguments are in raw (non-evaluated) form. (define (-arch-parse context name comment attrs default-alignment insn-lsb0? machs isas) (logit 2 "Processing arch " name " ...\n") (make (parse-name name context) (parse-comment comment context) (atlist-parse attrs "arch" context) (-arch-parse-alignment context default-alignment) (parse-boolean context insn-lsb0?) (-arch-parse-machs context machs) (-arch-parse-isas context isas)) ) ; Read an architecture description. ; This is the main routine for analyzing an arch description in the .cpu file. ; ARG-LIST is an associative list of field name and field value. ; parse-arch is invoked to create the `arch' object. (define -arch-read (lambda arg-list (let ((context "arch-read") ; object members and default values (name "unknown") (comment "") (attrs nil) (default-alignment 'aligned) (insn-lsb0? #f) (machs #f) (isas #f) ) ; Loop over each element in ARG-LIST, recording what's found. (let loop ((arg-list arg-list)) (if (null? arg-list) nil (let ((arg (car arg-list)) (elm-name (caar arg-list))) (case elm-name ((name) (set! name (cadr arg))) ((comment) (set! comment (cadr arg))) ((attrs) (set! attrs (cdr arg))) ((default-alignment) (set! default-alignment (cadr arg))) ((insn-lsb0?) (set! insn-lsb0? (cadr arg))) ((machs) (set! machs (cdr arg))) ((isas) (set! isas (cdr arg))) (else (parse-error context "invalid arch arg" arg))) (loop (cdr arg-list))))) ; Ensure required fields are present. (if (not machs) (parse-error context "missing machs spec")) (if (not isas) (parse-error context "missing isas spec")) ; Now that we've identified the elements, build the object. (-arch-parse context name comment attrs default-alignment insn-lsb0? machs isas) ) ) ) ; Define an arch object, name/value pair list version. (define define-arch (lambda arg-list (let ((a (apply -arch-read arg-list))) (arch-set-data! CURRENT-ARCH a) (def-mach-attr! (adata-machs a)) (keep-mach-validate!) (def-isa-attr! (adata-isas a)) (keep-isa-validate!) ; Install the builtin objects now that we have an arch, and now that ; attributes MACH and ISA exist. (reader-install-builtin!) a)) ) ; Mach/isa processing. ; Create the MACH attribute. ; MACHS is the canonicalized machs spec to define-arch: (name . sanitize-key). (define (def-mach-attr! machs) (let ((mach-enums (append '((base)) (map (lambda (mach) (cons (car mach) (cons '- (if (cdr mach) (list (cons 'sanitize (cdr mach))) nil)))) machs) '((max))))) (define-attr '(type bitset) '(name MACH) '(comment "machine type selection") '(default base) (cons 'values mach-enums)) ) *UNSPECIFIED* ) ; Return #t if MACH is supported by OBJ. ; This is done by looking for the MACH attribute in OBJ. ; By definition, objects that support the default (base) mach support ; all machs. (define (mach-supports? mach obj) (let ((machs (bitset-attr->list (obj-attr-value obj 'MACH))) (name (obj:name mach))) (or (memq name machs) (memq 'base machs))) ;(let ((deflt (attr-lookup-default 'MACH obj))) ; (any-true? (map (lambda (m) (memq m deflt)) machs))))) ) ; Create the ISA attribute. ; ISAS is the canonicalized isas spec to define-arch: (name . sanitize-key). ; ISAS is a list of isa names. (define (def-isa-attr! isas) (let ((isa-enums (append (map (lambda (isa) (cons (car isa) (cons '- (if (cdr isa) (list (cons 'sanitize (cdr isa))) nil)))) isas) '((max))))) ; Using a bitset attribute here implies something could be used by two ; separate isas. This seems highly unlikely but we don't [as yet] ; preclude it. The other thing to consider is whether the cpu table ; would ever want to be opened for multiple isas. (define-attr '(type bitset) '(name ISA) '(comment "instruction set selection") ; If there's only one isa, don't (yet) pollute the tables with a value ; for it. (if (= (length isas) 1) '(for) '(for ifield operand insn hardware)) (cons 'values isa-enums)) ) *UNSPECIFIED* ) ; Return list of ISA names specified by OBJ. (define (obj-isa-list obj) (bitset-attr->list (obj-attr-value obj 'ISA)) ) ; Return #t if ISA is supported by OBJ. ; This is done by looking for the ISA attribute in OBJ. (define (isa-supports? isa obj) (let ((isas (obj-isa-list obj)) (name (obj:name isa))) (->bool (memq name isas))) ) ; The fetch/decode/execute process. ; "extract" is a fancy word for fetch/decode. ; FIXME: wip, not currently used. ; FIXME: move to inside define-isa, and maybe elsewhere. ; ;(defmacro ; define-extract (code) ; ;(arch-set-insn-extract! CURRENT-ARCH code) ; *UNSPECIFIED* ;) ; ;(defmacro ; define-execute (code) ; ;(arch-set-insn-execute! CURRENT-ARCH code) ; *UNSPECIFIED* ;) ; ISA specification. ; Each architecture is generally one isa, but in the case of ARM (and a few ; others) there is more than one. ; ; ??? "ISA" has a very well defined meaning, and our usage of it one might ; want to quibble over. A better name would be welcome. ; Associated with an instruction set is its framing. ; This refers to how instructions are laid out at the liw level (where several ; insns are framed together and executed sequentially or in parallel). ; ??? If one defines the term "format" as being how an individual instruction ; is laid out then formatting can be thought of as being different from ; framing. However, it's possible for a particular ISA to intertwine the two. ; Thus this will need to evolve. ; ??? Not used yet, wip. (define