; Simulator generator support routines. ; Copyright (C) 2000, 2001, 2002, 2006, 2009 Red Hat, Inc. ; This file is part of CGEN. ; One goal of this file is to provide cover functions for all methods. ; i.e. this file fills in the missing pieces of the interface between ; the application independent part of CGEN (i.e. the code loaded by read.scm) ; and the application dependent part (i.e. sim-*.scm). ; `send' is not intended to appear in sim-*.scm. ; [It still does but that's to be fixed.] ; Specify which application. (set! APPLICATION 'SIMULATOR) ; Cover functions for various methods. ; Return the C type of something. This isn't always a mode. (define (gen-type self) (send self 'gen-type)) ; Return the C type of an index's value or #f if not needed (scalar). (define (gen-index-type op sfmt) (let ((index-mode (send op 'get-index-mode))) (if index-mode (mode:c-type index-mode) #f)) ) ; Misc. state info. ; Currently supported options: ; with-scache ; generate code to use the scache ; This is an all or nothing option, either scache is used or it's not. ; with-profile fn|sw ; generate code to do profiling in the semantic function ; code (fn) or in the semantic switch (sw) ; with-generic-write ; For architectures that have parallel execution. ; Execute the semantics by recording the results in a generic buffer, ; and doing a post-semantics writeback pass. ; with-parallel-only ; Only generate parallel versions of each insn. ; with-multiple-isa ; Enable multiple-isa support (eg. arm+thumb). ; copyright fsf|redhat ; emit an FSF or Cygnus copyright (temporary, pending decision) ; package gnusim|cygsim ; indicate the software package ; #t if the scache is being used (define /with-scache? #f) (define (with-scache?) /with-scache?) ; #t if we're generating profiling code ; Each of the function and switch semantic code can have profiling. ; The options as passed are stored in /with-profile-{fn,sw}?, and ; /with-profile? is set at code generation time. (define /with-profile-fn? #f) (define /with-profile-sw? #f) (define /with-profile? #f) (define (with-profile?) /with-profile?) (define (with-any-profile?) (or /with-profile-fn? /with-profile-sw?)) ; #t if multiple isa support is enabled (define /with-multiple-isa? #f) (define (with-multiple-isa?) /with-multiple-isa?) ; Handle parallel execution with generic writeback pass. (define /with-generic-write? #f) (define (with-generic-write?) /with-generic-write?) ; Only generate parallel versions of each insn. (define /with-parallel-only? #f) (define (with-parallel-only?) /with-parallel-only?) ; String containing copyright text. (define CURRENT-COPYRIGHT #f) ; String containing text defining the package we're generating code for. (define CURRENT-PACKAGE #f) ; Initialize the options. (define (option-init!) (set! /with-scache? #f) (set! /with-profile-fn? #f) (set! /with-profile-sw? #f) (set! /with-multiple-isa? #f) (set! /with-generic-write? #f) (set! /with-parallel-only? #f) (set! CURRENT-COPYRIGHT copyright-fsf) (set! CURRENT-PACKAGE package-gnu-simulators) *UNSPECIFIED* ) ; Handle an option passed in from the command line. (define (option-set! name value) (case name ((with-scache) (set! /with-scache? #t)) ((with-profile) (cond ((equal? value '("fn")) (set! /with-profile-fn? #t)) ((equal? value '("sw")) (set! /with-profile-sw? #t)) (else (error "invalid with-profile value" value)))) ((with-multiple-isa) (set! /with-multiple-isa? #t)) ((with-generic-write) (set! /with-generic-write? #t)) ((with-parallel-only) (set! /with-parallel-only? #t)) ((copyright) (cond ((equal? value '("fsf")) (set! CURRENT-COPYRIGHT copyright-fsf)) ((equal? value '("redhat")) (set! CURRENT-COPYRIGHT copyright-red-hat)) (else (error "invalid copyright value" value)))) ((package) (cond ((equal? value '("gnusim")) (set! CURRENT-PACKAGE package-gnu-simulators)) ((equal? value '("cygsim")) (set! CURRENT-PACKAGE package-red-hat-simulators)) (else (error "invalid package value" value)))) (else (error "unknown option" name)) ) *UNSPECIFIED* ) ; #t if the cpu can execute insns parallely. ; This one isn't passed on the command line, but we follow the convention ; of prefixing these things with `with-'. ; While processing operand reading (or writing), parallel execution support ; needs to be turned off, so it is up to the appropriate cgen-foo.c proc to ; set-with-parallel?! appropriately. (define /with-parallel? #f) (define (with-parallel?) /with-parallel?) (define (set-with-parallel?! flag) (set! /with-parallel? flag)) ; Kind of parallel support. ; If 'read, read pre-processing is done. ; If 'write, write post-processing is done. ; ??? At present we always use write post-processing, though the previous ; version used read pre-processing. Not sure supporting both is useful ; in the long run. (define /with-parallel-kind 'write) ; #t if parallel support is provided by read pre-processing. (define (with-parallel-read?) (and /with-parallel? (eq? /with-parallel-kind 'read)) ) ; #t if parallel support is provided by write post-processing. (define (with-parallel-write?) (and /with-parallel? (eq? /with-parallel-kind 'write)) ) ; Misc. utilities. ; All machine generated cpu elements are accessed through a cover macro ; to hide the details of the underlying implementation. (define c-cpu-macro "CPU") (define (gen-cpu-ref sym) (string-append c-cpu-macro " (" sym ")") ) ; Return C code to fetch a value from instruction memory. ; PC-VAR is the C expression containing the address of the start of the ; instruction. ; ??? Aligned/unaligned support? (define (gen-ifetch pc-var bitoffset bitsize) (string-append "GETIMEM" (case bitsize ((8) "UQI") ((16) "UHI") ((32) "USI") (else (error "bad bitsize argument to gen-ifetch" bitsize))) " (current_cpu, " pc-var " + " (number->string (quotient bitoffset 8)) ")") ) ; Instruction field support code. ; Return a object of the value of an ifield. (define (/cxmake-ifld-val mode f) (if (with-scache?) ; ??? Perhaps a better way would be to defer evaluating the src of a ; set until the method processing the dest. (cx:make-with-atlist mode (gen-ifld-argbuf-ref f) (atlist-make "" (bool-attr-make 'CACHED #t))) (cx:make mode (gen-extracted-ifld-value f))) ) ; Type system. ; Methods: ; gen-type - return C code representing the type ; gen-sym-defn - generate decl using the provided symbol ; gen-sym-get-macro - generate GET macro for accessing CPU elements ; gen-sym-set-macro - generate SET macro for accessing CPU elements ; Scalar type (method-make! 'gen-type (lambda (self) (mode:c-type (elm-get self 'mode))) ) (method-make! 'gen-sym-defn (lambda (self sym comment) (string-append " /* " comment " */\n" " " (send self 'gen-type) " " (gen-c-symbol sym) ";\n")) ) (method-make! 'gen-sym-get-macro (lambda (self sym comment) (let ((sym (gen-c-symbol sym))) (gen-get-macro sym "" (gen-cpu-ref sym)))) ) (method-make! 'gen-sym-set-macro (lambda (self sym comment) (let ((sym (gen-c-symbol sym))) (gen-set-macro sym "" (gen-cpu-ref sym)))) ) (method-make! 'gen-ref (lambda (self sym index estate) sym)) ; Array type (method-make! 'gen-type (lambda (self) (mode:c-type (elm-get self 'mode))) ) (method-make! 'gen-sym-defn (lambda (self sym comment) (string-append " /* " comment " */\n" " " (send self 'gen-type) " " (gen-c-symbol sym) (gen-array-ref (elm-get self 'dimensions)) ";\n") ) ) (method-make! 'gen-sym-get-macro (lambda (self sym comment) (let ((sym (gen-c-symbol sym)) (rank (length (elm-get self 'dimensions)))) (string-append "#define GET_" (string-upcase sym) "(" (string-drop 2 (gen-macro-args rank)) ") " (gen-cpu-ref sym) (gen-array-ref (macro-args rank)) "\n" ))) ) (method-make! 'gen-sym-set-macro (lambda (self sym comment) (let ((sym (gen-c-symbol sym)) (rank (length (elm-get self 'dimensions)))) (string-append "#define SET_" (string-upcase sym) "(" (string-drop 2 (gen-macro-args rank)) ", x) " "(" (gen-cpu-ref sym) (gen-array-ref (macro-args rank)) " = (x))\n" ))) ) ; Return a reference to the array. ; SYM is the name of the array. ; INDEX is either a single index object or a (possibly empty) list of objects, ; one object per dimension. (method-make! 'gen-ref (lambda (self sym index estate) (let ((gen-index1 (lambda (idx) (string-append "[" (/gen-hw-index idx estate) "]")))) (string-append sym (cond ((list? index) (string-map gen-index1 index)) (else (gen-index1 index)))))) ) ; Integers ; ;(method-make! ; 'gen-type ; (lambda (self) ; (mode:c-type (mode-find (elm-get self 'bits) ; (if (has-attr? self 'UNSIGNED) ; 'UINT 'INT))) ; ) ;) ; ;(method-make! 'gen-sym-defn (lambda (self sym comment) "")) ;(method-make! 'gen-sym-get-macro (lambda (self sym comment) "")) ;(method-make! 'gen-sym-set-macro (lambda (self sym comment) "")) ; Hardware descriptions support code. ; ; Various operations are required for each h/w object to support the various ; things the simulator will want to do with it. ; ; Methods: ; gen-type - C type to use to record value, as a string. ; ??? Delete and just use get-mode? ; gen-defn - generate a definition of the h/w element ; gen-get-macro - Generate definition of the GET access macro. ; gen-set-macro - Generate definition of the SET access macro. ; gen-write - Same as gen-read except done on output operands ; cxmake-get - Return a object to fetch the value. ; gen-set-quiet - Set the value. ; ??? Could just call this gen-set as there is no gen-set-trace ; but for consistency with the messages passed to operands ; we use this same. ; save-index? - return #t if an index needs to be saved for parallel ; execution post-write processing ; gen-profile-decl ; gen-record-profile ; get-mode ; gen-profile-locals ; gen-sym-get-macro - Generate default GET access macro. ; gen-sym-set-macro - Generate default SET access macro. ; gen-ref - Return a C reference to the object. ; gen-type handler, must be overridden. (method-make! 'gen-type (lambda (self) (error "gen-type not overridden:" self)) ) ; Generate CPU state struct entries, must be overridden. (method-make! 'gen-defn (lambda (self) (error "gen-defn not overridden:" self)) ) (method-make! 'gen-sym-decl (lambda (self sym comment) "")) ; Return a C reference to a hardware object. (method-make! 'gen-ref (lambda (self sym index estate) sym)) ; Each hardware type must provide its own gen-write method. (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) (error "gen-write method not overridden:" self)) ) (method-make! 'gen-profile-decl (lambda (self) "")) ; Default gen-record-profile method. (method-make! 'gen-record-profile (lambda (self index sfmt estate) "") ; nothing to do ) ; Default cxmake-get method. ; Return a object of the value of SELF. ; ESTATE is the current rtl evaluator state. ; INDEX is a object. It must be an ifield. ; SELECTOR is a hardware selector RTX. (method-make! 'cxmake-get (lambda (self estate mode index selector) (if (not (eq? 'ifield (hw-index:type index))) (error "not an ifield hw-index" index)) (/cxmake-ifld-val mode (hw-index:value index))) ) ; Handle gen-get-macro/gen-set-macro. (method-make! 'gen-get-macro (lambda (self) "") ) (method-make! 'gen-set-macro (lambda (self) "") ) ; PC support ; 'gen-set-quiet helper for PC values. ; NEWVAL is a object of the value to be assigned. ; If OPTIONS contains #:direct, set the PC directly, bypassing semantic ; code considerations. ; ??? OPTIONS support wip. Probably want a new form (or extend existing form) ; of rtx: that takes a variable number of named arguments. ; ??? Another way to get #:direct might be (raw-reg h-pc). (define (/hw-gen-set-quiet-pc self estate mode index selector newval . options) (if (not (send self 'pc?)) (error "Not a PC:" self)) (cond ((memq #:direct options) (/hw-gen-set-quiet self estate mode index selector newval)) ((has-attr? newval 'CACHED) (string-append "SEM_BRANCH_VIA_CACHE (current_cpu, sem_arg, " (cx:c newval) ", vpc);\n")) (else (string-append "SEM_BRANCH_VIA_ADDR (current_cpu, sem_arg, " (cx:c newval) ", vpc);\n"))) ) (method-make! 'gen-set-quiet /hw-gen-set-quiet-pc) ; Handle updates of the pc during parallel execution. ; This is done in a post-processing pass after semantic evaluation. ; SFMT is the . ; OP is the operand. ; ACCESS-MACRO is the runtime C macro to use to fetch indices computed ; during semantic evaluation. ; ; ??? This wouldn't be necessary if gen-set-quiet were a virtual method. ; At this point I'm reluctant to willy nilly make methods virtual. (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) (string-append " " (send self 'gen-set-quiet estate VOID index hw-selector-default (cx:make DFLT (string-append access-macro " (" (gen-sym op) ")"))))) ) (method-make! 'cxmake-skip (lambda (self estate yes?) (cx:make VOID (string-append "if (" yes? ")\n" " SEM_SKIP_INSN (current_cpu, sem_arg, vpc);\n"))) ) ; Registers. (method-make-forward! 'type '(gen-type)) (method-make! 'gen-defn (lambda (self) (send (elm-get self 'type) 'gen-sym-defn (obj:name self) (obj:comment self))) ) (method-make-forward! 'type '(gen-ref gen-sym-get-macro gen-sym-set-macro)) ; For parallel instructions supported by queueing outputs for later update, ; return a boolean indicating if an index needs to be recorded. ; An example of when the index isn't needed is if the index can be determined ; during extraction. (method-make! 'save-index? (lambda (self op) ; FIXME: Later handle case where register number is determined at runtime. #f) ) ; Handle updates of registers during parallel execution. ; This is done in a post-processing pass after semantic evaluation. ; SFMT is the . ; OP is the . ; ACCESS-MACRO is the runtime C macro to use to fetch indices computed ; during semantic evaluation. ; FIXME: May need mode of OP. (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) ; First get a hw-index object to use during indexing. ; Some indices, e.g. memory addresses, are computed during semantic ; evaluation. Others are computed during the extraction phase. (let ((index (send index 'get-write-index self sfmt op access-macro))) (string-append " " (send self 'gen-set-quiet estate mode index hw-selector-default (cx:make DFLT (string-append access-macro " (" (gen-sym op) ")")))))) ) (method-make! 'gen-profile-decl (lambda (self) (string-append " /* " (obj:comment self) " */\n" " unsigned long " (gen-c-symbol (obj:name self)) ";\n")) ) (method-make! 'gen-record-profile (lambda (self index sfmt estate) ; FIXME: Need to handle scalars. (/gen-hw-index-raw index estate)) ) (method-make! 'gen-get-macro (lambda (self) (let ((getter (elm-get self 'get)) (mode (send self 'get-mode))) (if getter (let ((args (car getter)) (expr (cadr getter))) (gen-get-macro2 (gen-sym self) (if (hw-scalar? self) "" "index") (rtl-c mode #f ;; h/w is not ISA-specific (if (hw-scalar? self) nil (list (list (car args) 'UINT "index"))) expr #:rtl-cover-fns? #t #:macro? #t))) (send self 'gen-sym-get-macro (obj:name self) (obj:comment self))))) ) (method-make! 'gen-set-macro (lambda (self) (let ((setter (elm-get self 'set)) (mode (send self 'get-mode))) (if setter (let ((args (car setter)) (expr (cadr setter))) (gen-set-macro2 (gen-sym self) (if (hw-scalar? self) "" "index") "x" (rtl-c VOID ;; not `mode', sets have mode VOID #f ;; h/w is not ISA-specific (if (hw-scalar? self) (list (list (car args) (hw-mode self) "(x)")) (list (list (car args) 'UINT "(index)") (list (cadr args) (hw-mode self) "(x)"))) expr #:rtl-cover-fns? #t #:macro? #t))) (send self 'gen-sym-set-macro (obj:name self) (obj:comment self))))) ) ; Utility to build a object to fetch the value of a register. (define (/hw-cxmake-get hw estate mode index selector) (let ((mode (if (mode:eq? 'DFLT mode) (send hw 'get-mode) mode)) (getter (hw-getter hw))) ; If the register is accessed via a cover function/macro, do it. ; Otherwise fetch the value from the cached address or from the CPU struct. (cx:make mode (cond (getter (let ((scalar? (hw-scalar? hw)) (c-index (/gen-hw-index index estate))) (string-append "GET_" (string-upcase (gen-sym hw)) " (" (if scalar? "" c-index) ")"))) ((and (hw-cache-addr? hw) ; FIXME: redo test (eq? 'ifield (hw-index:type index))) (string-append "* " (if (with-scache?) (gen-hw-index-argbuf-ref index) (gen-hw-index-argbuf-name index)))) (else (gen-cpu-ref (send hw 'gen-ref (gen-sym hw) index estate)))))) ) (method-make! 'cxmake-get /hw-cxmake-get) ; raw-reg: support ; ??? raw-reg: support is wip (method-make! 'cxmake-get-raw (lambda (self estate mode index selector) (let ((mode (if (mode:eq? 'DFLT mode) (send self 'get-mode) mode))) (cx:make mode (gen-cpu-ref (send self 'gen-ref (gen-sym self) index estate))))) ) ; Utilities to generate C code to assign a variable to a register. (define (/hw-gen-set-quiet hw estate mode index selector newval) (let ((setter (hw-setter hw))) (cond (setter (let ((scalar? (hw-scalar? hw)) (c-index (/gen-hw-index index estate))) (string-append "SET_" (string-upcase (gen-sym hw)) " (" (if scalar? "" (string-append c-index ", ")) (cx:c newval) ");\n"))) ((and (hw-cache-addr? hw) ; FIXME: redo test (eq? 'ifield (hw-index:type index))) (string-append "* " (if (with-scache?) (gen-hw-index-argbuf-ref index) (gen-hw-index-argbuf-name index)) " = " (cx:c newval) ";\n")) (else (string-append (gen-cpu-ref (send hw 'gen-ref (gen-sym hw) index estate)) " = " (cx:c newval) ";\n")))) ) (method-make! 'gen-set-quiet /hw-gen-set-quiet) ; raw-reg: support ; ??? wip (method-make! 'gen-set-quiet-raw (lambda (self estate mode index selector newval) (string-append (gen-cpu-ref (send self 'gen-ref (gen-sym self) index estate)) " = " (cx:c newval) ";\n")) ) ; Return name of C access function for getting/setting a register. (define (gen-reg-getter-fn hw prefix) (string-append prefix "_" (gen-sym hw) "_get") ) (define (gen-reg-setter-fn hw prefix) (string-append prefix "_" (gen-sym hw) "_set") ) ; Generate decls for access fns of register HW, beginning with ; PREFIX, using C type TYPE. ; SCALAR? is #t if the register is a scalar. Otherwise it is #f and the ; register is a bank of registers. (define (gen-reg-access-decl hw prefix type scalar?) (string-append type " " (gen-reg-getter-fn hw prefix) " (SIM_CPU *" (if scalar? "" ", UINT") ");\n" "void " (gen-reg-setter-fn hw prefix) " (SIM_CPU *, " (if scalar? "" "UINT, ") type ");\n" ) ) ; Generate defns of access fns of register HW, beginning with ; PREFIX, using C type TYPE. ; SCALAR? is #t if the register is a scalar. Otherwise it is #f and the ; register is a bank of registers. ; GET/SET-CODE are C fragments to get/set the value. ; ??? Inlining left for later. (define (gen-reg-access-defn hw prefix type scalar? get-code set-code) (string-append "/* Get the value of " (obj:str-name hw) ". */\n\n" type "\n" (gen-reg-getter-fn hw prefix) " (SIM_CPU *current_cpu" (if scalar? "" ", UINT regno") ")\n{\n" get-code "}\n\n" "/* Set a value for " (obj:str-name hw) ". */\n\n" "void\n" (gen-reg-setter-fn hw prefix) " (SIM_CPU *current_cpu, " (if scalar? "" "UINT regno, ") type " newval)\n" "{\n" set-code "}\n\n") ) ; Memory support. (method-make! 'cxmake-get (lambda (self estate mode index selector) (let ((mode (if (mode:eq? 'DFLT mode) (hw-mode self) mode)) (default-selector? (hw-selector-default? selector))) (cx:make mode (string-append "GETMEM" (obj:str-name mode) (if default-selector? "" "ASI") " (" "current_cpu, pc, " (/gen-hw-index index estate) (if default-selector? "" (string-append ", " (/gen-hw-selector selector))) ")")))) ) (method-make! 'gen-set-quiet (lambda (self estate mode index selector newval) (let ((mode (if (mode:eq? 'DFLT mode) (hw-mode self) mode)) (default-selector? (hw-selector-default? selector))) (string-append "SETMEM" (obj:str-name mode) (if default-selector? "" "ASI") " (" "current_cpu, pc, " (/gen-hw-index index estate) (if default-selector? "" (string-append ", " (/gen-hw-selector selector))) ", " (cx:c newval) ");\n"))) ) (method-make-forward! 'type '(gen-type)) (method-make! 'gen-defn (lambda (self sym comment) "")) (method-make! 'gen-sym-get-macro (lambda (self sym comment) "")) (method-make! 'gen-sym-set-macro (lambda (self sym comment) "")) ; For parallel instructions supported by queueing outputs for later update, ; return the type of the index or #f if not needed. (method-make! 'save-index? (lambda (self op) ; In the case of the complete memory address being an immediate ; argument, we can return #f (later). AI) ) (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) (let ((index (send index 'get-write-index self sfmt op access-macro))) (string-append " " (send self 'gen-set-quiet estate mode index hw-selector-default (cx:make DFLT (string-append access-macro " (" (gen-sym op) ")")))))) ) ; Immediates, addresses. (method-make-forward! 'type '(gen-type)) (method-make! 'gen-defn (lambda (self) (send (elm-get self 'type) 'gen-sym-defn (obj:name self) (obj:comment self))) ) (method-make-forward! 'type '(gen-sym-get-macro gen-sym-set-macro)) (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) (error "gen-write of shouldn't happen")) ) ;; FIXME (method-make! 'gen-type (lambda (self) "ADDR")) (method-make! 'gen-defn (lambda (self sym comment) "")) (method-make! 'gen-sym-get-macro (lambda (self sym comment) "")) (method-make! 'gen-sym-set-macro (lambda (self sym comment) "")) ; Return a object of the value of SELF. ; ESTATE is the current rtl evaluator state. ; INDEX is a hw-index object. It must be an ifield. ; Needed because we record our own copy of the ifield in ARGBUF. ; SELECTOR is a hardware selector RTX. (method-make! 'cxmake-get (lambda (self estate mode index selector) (if (not (eq? 'ifield (hw-index:type index))) (error "not an ifield hw-index" index)) (if (with-scache?) (cx:make mode (gen-hw-index-argbuf-ref index)) (cx:make mode (gen-hw-index-argbuf-name index)))) ) (method-make! 'gen-write (lambda (self estate index mode sfmt op access-macro) (error "gen-write of shouldn't happen")) ) ;; FIXME: consistency says there should be gen-defn, gen-sym-[gs]et-macro (method-make! 'gen-type (lambda (self) "IADDR")) ; Return a object of the value of SELF. ; ESTATE is the current rtl evaluator state. ; INDEX is a object. It must be an ifield. ; Needed because we record our own copy of the ifield in ARGBUF, ; *and* because we want to record in the result the 'CACHED attribute ; since instruction addresses based on ifields are fixed [and thus cacheable]. ; SELECTOR is a hardware selector RTX. (method-make! 'cxmake-get (lambda (self estate mode index selector) (if (not (eq? 'ifield (hw-index:type index))) (error "not an ifield hw-index" index)) (if (with-scache?) ; ??? Perhaps a better way would be to defer evaluating the src of a ; set until the method processing the dest. (cx:make-with-atlist mode (gen-hw-index-argbuf-ref index) (atlist-make "" (bool-attr-make 'CACHED #t))) (cx:make mode (gen-hw-index-argbuf-name index)))) ) ; Hardware index support code. ; Return the index to use by the gen-write method. ; In the cases where this is needed (the index isn't known until insn ; execution time), the index is computed along with the value to be stored, ; so this is easy. (method-make! 'get-write-index (lambda (self hw sfmt op access-macro) (if (memq (hw-index:type self) '(scalar constant enum str-expr ifield)) self (let ((index-mode (send hw 'get-index-mode))) (if index-mode (make 'anonymous 'str-expr index-mode (string-append access-macro " (" (/op-index-name op) ")")) (hw-index-scalar))))) ) ; Return the name of the PAREXEC structure member holding a hardware index ; for operand OP. (define (/op-index-name op) (string-append (gen-sym op) "_idx") ) ; Cover fn to hardware indices to generate the actual C code. ; INDEX is the hw-index object (i.e. op:index). ; The result is a string of C code. ; FIXME:wip (define (/gen-hw-index-raw index estate) (let ((type (hw-index:type index)) (mode (hw-index:mode index)) (value (hw-index:value index))) (case type ((scalar) "") ; special case UINT to cut down on unnecessary verbosity. ; ??? May wish to handle more similarily. ((constant) (if (mode:eq? 'UINT mode) (number->string value) (string-append "((" (mode:c-type mode) ") " (number->string value) ")"))) ((enum) (let ((sym (hw-index-enum-name index)) (obj (hw-index-enum-obj index))) (gen-enum-sym obj sym))) ((str-expr) value) ((rtx) (rtl-c-with-estate estate mode value)) ((ifield) (if (= (ifld-length value) 0) "" (gen-extracted-ifld-value value))) ((operand) (cx:c (send value 'cxmake-get estate mode (op:index value) (op:selector value) #f))) (else (error "/gen-hw-index-raw: invalid index:" index)))) ) ; Same as /gen-hw-index-raw except used where speedups are possible. ; e.g. doing array index calcs at extraction time. (define (/gen-hw-index index estate) (let ((type (hw-index:type index)) (mode (hw-index:mode index)) (value (hw-index:value index))) (case type ((scalar) "") ((constant) (string-append "((" (mode:c-type mode) ") " (number->string value) ")")) ((enum) (let ((sym (hw-index-enum-name index)) (obj (hw-index-enum-obj index))) (gen-enum-sym obj sym))) ((str-expr) value) ((rtx) (rtl-c-with-estate estate mode value)) ((ifield) (if (= (ifld-length value) 0) "" (cx:c (/cxmake-ifld-val mode value)))) ((operand) (cx:c (send value 'cxmake-get estate mode (op:index value) (op:selector value)))) (else (error "/gen-hw-index: invalid index:" index)))) ) ; Return address where HW is stored. (define (/gen-hw-addr hw estate index) (let ((setter (hw-setter hw))) (cond ((and (hw-cache-addr? hw) ; FIXME: redo test (eq? 'ifield (hw-index:type index))) (if (with-scache?) (gen-hw-index-argbuf-ref index) (gen-hw-index-argbuf-name index))) (else (string-append "& " (gen-cpu-ref (send hw 'gen-ref (gen-sym hw) index estate)))))) ) ; Return a object of the value of a hardware index. (method-make! 'cxmake-get (lambda (self estate mode) (let ((mode (if (mode:eq? 'DFLT mode) (elm-get self 'mode) mode))) ; If MODE is VOID, abort. (if (mode:eq? 'VOID mode) (error "hw-index:cxmake-get: result needs a mode" self)) (cx:make (if (mode:host? mode) ; FIXME: Temporary hack to generate same code as before. (let ((xmode (object-copy-top mode))) (obj-cons-attr! xmode (bool-attr-make 'FORCE-C #t)) xmode) mode) (/gen-hw-index self estate)))) ) ; Hardware selector support code. ; Generate C code for SEL. (define (/gen-hw-selector sel) (rtl-c INT #f nil sel) ) ; Instruction operand support code. ; Methods: ; gen-type - Return C type to use to hold operand's value. ; gen-read - Record an operand's value prior to parallely executing ; several instructions. Not used if gen-write used. ; gen-write - Write back an operand's value after parallely executing ; several instructions. Not used if gen-read used. ; cxmake-get - Return C code to fetch the value of an operand. ; gen-set-quiet - Return C code to set the value of an operand. ; gen-set-trace - Return C code to set the value of an operand, and print ; a result trace message. ??? Ideally this will go away when ; trace record support is complete. ; Return the C type of an operand. ; Generally we forward things on to TYPE, but for the actual type we need to ; use the get-mode method. ;(method-make-forward! 'type '(gen-type)) (method-make! 'gen-type (lambda (self) ; First get the mode. (let ((mode (send self 'get-mode))) ; If it's VOID use the type's type. (if (mode:eq? 'DFLT mode) (send (op:type self) 'gen-type) (mode:c-type mode)))) ) ; Extra pc operand methods. (method-make! 'cxmake-get (lambda (self estate mode index selector) (let ((mode (if (mode:eq? 'DFLT mode) (send self 'get-mode) mode))) (logit 4 " cxmake-get self=" (obj:name self) " mode=" (obj:name mode) "\n") (if (obj-has-attr? self 'RAW) (let ((hw (op:type self)) ;; For consistency with process index,selector similarly. (index (if index index (op:index self))) (selector (if selector selector (op:selector self)))) (send hw 'cxmake-get-raw estate mode index selector)) ;; The enclosing function must set `pc' to the correct value. (cx:make mode "pc")))) ) (method-make! 'cxmake-skip (lambda (self estate yes?) (send (op:type self) 'cxmake-skip estate (rtl-c INT (obj-isa-list self) nil yes? #:rtl-cover-fns? #t))) ) ; For parallel write post-processing, we don't want to defer setting the pc. ; ??? Not sure anymore. ;(method-make! ; 'gen-set-quiet ; (lambda (self estate mode index selector newval) ; (/op-gen-set-quiet self estate mode index selector newval))) ;(method-make! ; 'gen-set-trace ; (lambda (self estate mode index selector newval) ; (/op-gen-set-trace self estate mode index selector newval))) ; Name of C macro to access parallel execution operand support. (define /par-operand-macro "OPRND") ; Return C code to fetch an operand's value and save it away for the ; semantic handler. This is used to handle parallel execution of several ; instructions where all inputs of all insns are read before any outputs are ; written. ; For operands, the word `read' is only used in this context. (define (op:read op sfmt) (let ((estate (estate-make-for-rtl-c nil))) (send op 'gen-read estate sfmt /par-operand-macro)) ) ; Return C code to write an operand's value. ; This is used to handle parallel execution of several instructions where all ; outputs are written to temporary spots first, and then a final ; post-processing pass is run to update cpu state. ; For operands, the word `write' is only used in this context. (define (op:write op sfmt) (let ((estate (estate-make-for-rtl-c nil))) (send op 'gen-write estate sfmt /par-operand-macro)) ) ; Default gen-read method. ; This is used to help support targets with parallel insns. ; Either this or gen-write (but not both) is used. (method-make! 'gen-read (lambda (self estate sfmt access-macro) (string-append " " access-macro " (" (gen-sym self) ") = " ; Pass #f for the index -> use the operand's builtin index. ; Ditto for the selector. (cx:c (send self 'cxmake-get estate DFLT #f #f)) ";\n")) ) ; Forward gen-write onto the object. (method-make! 'gen-write (lambda (self estate sfmt access-macro) (let ((write-back-code (send (op:type self) 'gen-write estate (op:index self) (op:mode self) sfmt self access-macro))) ; If operand is conditionally written, we have to check that first. ; ??? If two (or more) operands are written based on the same condition, ; all the tests can be collapsed together. Not sure that's a big ; enough win yet. (if (op:cond? self) (string-append " if (written & (1 << " (number->string (op:num self)) "))\n" " {\n" " " write-back-code " }\n") write-back-code))) ) ; Return object to get the value of an operand. ; ESTATE is the current rtl evaluator state. ; If INDEX is non-#f use it, otherwise use (op:index self). ; This special handling of #f for INDEX is *only* supported for operands ; in cxmake-get, gen-set-quiet, and gen-set-trace. ; Ditto for SELECTOR. (method-make! 'cxmake-get (lambda (self estate mode index selector) (let ((mode (if (mode:eq? 'DFLT mode) (send self 'get-mode) mode)) (index (if index index (op:index self))) (selector (if selector selector (op:selector self)))) ;; If the instruction could be parallely executed with others and we're ;; doing read pre-processing, the operand has already been fetched, we ;; just have to grab the cached value. (let ((result (cond ((obj-has-attr? self 'RAW) (send (op:type self) 'cxmake-get-raw estate mode index selector)) ((with-parallel-read?) (cx:make-with-atlist mode (string-append /par-operand-macro " (" (gen-sym self) ")") nil)) ;; FIXME: want CACHED attr if present ((op:getter self) (let ((args (car (op:getter self))) (expr (cadr (op:getter self)))) (rtl-c-expr mode (obj-isa-list self) (if (= (length args) 0) nil (list (list (car args) 'UINT index))) expr #:rtl-cover-fns? #t))) (else (send (op:type self) 'cxmake-get estate mode index selector))))) (logit 4 " cxmake-get self=" (obj:name self) " mode=" (obj:name mode) " index=" (obj:name index) " selector=" selector "\n") result))) ) ; Utilities to implement gen-set-quiet/gen-set-trace. (define (/op-gen-set-quiet op estate mode index selector newval) (send (op:type op) 'gen-set-quiet estate mode index selector newval) ) ; Return C code to call the appropriate queued-write handler. ; ??? wip (define (/op-gen-queued-write op estate mode index selector newval) (let* ((hw (op:type op)) (setter (hw-setter hw)) (sem-mode (mode:sem-mode mode))) (string-append " " "sim_queue_" ; FIXME: clean up (pc? op) vs (memory? hw) ; FIXME: (send 'pc?) is a temporary hack, (pc? op) didn't work (cond ((send hw 'pc?) (string-append (if setter "fn_" "") "pc")) (else (string-append (cond ((memory? hw) "mem_") ((hw-scalar? hw) "scalar_") (else "")) (if setter "fn_" "") (string-downcase (symbol->string (if sem-mode (mode-real-name sem-mode) (mode-real-name mode))))))) "_write (current_cpu" ; ??? May need to include h/w id some day. (if setter (string-append ", " (gen-reg-setter-fn hw "@cpu@")) "") (cond ((hw-scalar? hw) "") (setter (string-append ", " (/gen-hw-index index estate))) ((memory? hw) (string-append ", " (/gen-hw-index index estate))) (else (string-append ", " (/gen-hw-addr (op:type op) estate index)))) ", " newval ");\n")) ) (define (/op-gen-set-quiet-parallel op estate mode index selector newval) (if (with-generic-write?) (/op-gen-queued-write op estate mode index selector (cx:c newval)) (string-append (if (op-save-index? op) (string-append " " /par-operand-macro " (" (/op-index-name op) ")" " = " (/gen-hw-index index estate) ";\n") "") " " /par-operand-macro " (" (gen-sym op) ")" " = " (cx:c newval) ";\n")) ) (define /operand-number-elaboration-written? #f) ;; Return code to update `written'. (define (/op-gen-written-update op) (if (op:cond? op) ;; FIXME: we don't yet handle a large number of operands (if (< (op:num op) 32) (string-append " written |= (1 << " (number->string (op:num op)) ");\n") (begin ;; FIXME: This creates broken simulators if with-parallel-write?. ;; (message (if (with-parallel-write?) "Error: " "Warning: ") ;; (obj:name op) ;; " operand number " (op:num op) ;; " is too large (>= 32)\n") (if (not /operand-number-elaboration-written?) (begin (message "This is a current internal cgen limitation.\n") (if (not (with-parallel-write?)) (message "The only effect is a loss in profiling capability.\n")) (set! /operand-number-elaboration-written? #t))) "")) "") ) (define (/op-gen-set-trace op estate mode index selector newval) (string-append " {\n" " " (mode:c-type mode) " opval = " (cx:c newval) ";\n" ; Dispatch to setter code if appropriate " " (if (op:setter op) (let ((args (car (op:setter op))) (expr (cadr (op:setter op)))) (rtl-c VOID (obj-isa-list op) (if (= (length args) 0) (list (list 'newval mode "opval")) (list (list (car args) 'UINT index) (list 'newval mode "opval"))) expr #:rtl-cover-fns? #t)) ;else (send (op:type op) 'gen-set-quiet estate mode index selector (cx:make-with-atlist mode "opval" (cx:atlist newval)))) (/op-gen-written-update op) ; TRACE_RESULT_ (cpu, abuf, hwnum, opnum, value); ; For each insn record array of operand numbers [or indices into ; operand instance table]. ; Could just scan the operand table for the operand or hardware number, ; assuming the operand number is stored in `op'. " TRACE_RESULT (current_cpu, abuf" ", " (send op 'gen-pretty-name mode) ", " (mode:printf-type mode) ", opval);\n" " }\n") ) (define (/op-gen-set-trace-parallel op estate mode index selector newval) (string-append " {\n" " " (mode:c-type mode) " opval = " (cx:c newval) ";\n" (if (with-generic-write?) (/op-gen-queued-write op estate mode index selector "opval") (string-append (if (op-save-index? op) (string-append " " /par-operand-macro " (" (/op-index-name op) ")" " = " (/gen-hw-index index estate) ";\n") "") " " /par-operand-macro " (" (gen-sym op) ")" " = opval;\n")) (/op-gen-written-update op) ; TRACE_RESULT_ (cpu, abuf, hwnum, opnum, value); ; For each insn record array of operand numbers [or indices into ; operand instance table]. ; Could just scan the operand table for the operand or hardware number, ; assuming the operand number is stored in `op'. " TRACE_RESULT (current_cpu, abuf" ", " (send op 'gen-pretty-name mode) ", " (mode:printf-type mode) ", opval);\n" " }\n") ) ; Return C code to set the value of an operand. ; NEWVAL is a object of the value to store. ; If INDEX is non-#f use it, otherwise use (op:index self). ; This special handling of #f for INDEX is *only* supported for operands ; in cxmake-get, gen-set-quiet, and gen-set-trace. ; Ditto for SELECTOR. (method-make! 'gen-set-quiet (lambda (self estate mode index selector newval) (let ((mode (if (mode:eq? 'DFLT mode) (send self 'get-mode) mode)) (index (if index index (op:index self))) (selector (if selector selector (op:selector self)))) ; ??? raw-reg: support wip (cond ((obj-has-attr? self 'RAW) (send (op:type self) 'gen-set-quiet-raw estate mode index selector newval)) ((with-parallel-write?) (/op-gen-set-quiet-parallel self estate mode index selector newval)) (else (/op-gen-set-quiet self estate mode index selector newval))))) ) ; Return C code to set the value of an operand and print TRACE_RESULT message. ; NEWVAL is a object of the value to store. ; If INDEX is non-#f use it, otherwise use (op:index self). ; This special handling of #f for INDEX is *only* supported for operands ; in cxmake-get, gen-set-quiet, and gen-set-trace. ; Ditto for SELECTOR. (method-make! 'gen-set-trace (lambda (self estate mode index selector newval) (let ((mode (if (mode:eq? 'DFLT mode) (send self 'get-mode) mode)) (index (if index index (op:index self))) (selector (if selector selector (op:selector self)))) ; ??? raw-reg: support wip (cond ((obj-has-attr? self 'RAW) (send (op:type self) 'gen-set-quiet-raw estate mode index selector newval)) ((with-parallel-write?) (/op-gen-set-trace-parallel self estate mode index selector newval)) (else (/op-gen-set-trace self estate mode index selector newval))))) ) ; Define and undefine C macros to tuck away details of instruction format used ; in the parallel execution functions. See gen-define-field-macro for a ; similar thing done for extraction/semantic functions. (define (gen-define-parallel-operand-macro sfmt) (string-append "#define " /par-operand-macro "(f) " "par_exec->operands." (gen-sym sfmt) ".f\n") ) (define (gen-undef-parallel-operand-macro sfmt) (string-append "#undef " /par-operand-macro "\n") ) ; Operand profiling and parallel execution support. (method-make! 'save-index? (lambda (self) (send (op:type self) 'save-index? self)) ) ; Return boolean indicating if operand OP needs its index saved ; (for parallel write post-processing support). (define (op-save-index? op) (send op 'save-index?) ) ; Return C code to record profile data for modeling use. ; In the case of a register, this is usually the register's number. ; This shouldn't be called in the case of a scalar, the code should be ; smart enough to know there is no need. (define (op:record-profile op sfmt out?) (let ((estate (estate-make-for-rtl-c nil))) (send op 'gen-record-profile sfmt out? estate)) ) ; Return C code to record the data needed for profiling operand SELF. ; This is done during extraction. (method-make! 'gen-record-profile (lambda (self sfmt out? estate) (if (hw-scalar? (op:type self)) "" (string-append " " (gen-argbuf-ref (send self 'sbuf-profile-sym out?)) " = " (send (op:type self) 'gen-record-profile (op:index self) sfmt estate) ";\n"))) ) ; Return C code to track profiling of operand SELF. ; This is usually called by the x-after handler. (method-make! 'gen-profile-code (lambda (self insn out?) (string-append " " "@cpu@_model_mark_" (if out? "set_" "get_") (gen-sym (op:type self)) " (current_cpu" (if (hw-scalar? (op:type self)) "" (string-append ", " (gen-argbuf-ref (send self 'sbuf-profile-sym out?)))) ");\n")) ) ; CPU, mach, model support. ; Return the declaration of the cpu/insn enum. (define (gen-cpu-insn-enum-decl cpu insn-list) (gen-enum-decl "@prefix@_insn_type" "instructions in cpu family @cpu@" "@PREFIX@_INSN_" (append! (map (lambda (i) (cons (obj:name i) (cons '- (atlist-attrs (obj-atlist i))))) insn-list) (if (with-parallel?) (apply append! (map (lambda (i) (list (cons (symbol-append 'par- (obj:name i)) (cons '- (atlist-attrs (obj-atlist i)))) (cons (symbol-append 'write- (obj:name i)) (cons '- (atlist-attrs (obj-atlist i)))))) (parallel-insns insn-list))) nil) (list '(-max)))) ) ; Return the enum of INSN in cpu family CPU. ; In addition to CGEN_INSN_TYPE, an enum is created for each insn in each ; cpu family. This collapses the insn enum space for each cpu to increase ; cache efficiently (since the IDESC table is similarily collapsed). (define (gen-cpu-insn-enum cpu insn) (string-upcase (string-append "@PREFIX@_INSN_" (gen-sym insn))) ) ; Return C code to declare the machine data. (define (/gen-mach-decls) (string-append (string-map (lambda (mach) (gen-obj-sanitize mach (string-append "extern const MACH " (gen-sym mach) "_mach;\n"))) (current-mach-list)) "\n") ) ; Return C code to define the machine data. (define (/gen-mach-data) (string-append "const MACH *sim_machs[] =\n{\n" (string-map (lambda (mach) (gen-obj-sanitize mach (string-append "#ifdef " (gen-have-cpu (mach-cpu mach)) "\n" " & " (gen-sym mach) "_mach,\n" "#endif\n"))) (current-mach-list)) " 0\n" "};\n\n" ) ) ; Return C declarations of cpu model support stuff. ; ??? This goes in arch.h but a better place is each cpu.h. (define (/gen-arch-model-decls) (string-append (gen-enum-decl 'model_type "model types" "MODEL_" (append (map (lambda (model) (cons (obj:name model) (cons '- (atlist-attrs (obj-atlist model))))) (current-model-list)) '((max)))) "#define MAX_MODELS ((int) MODEL_MAX)\n\n" (gen-enum-decl 'unit_type "unit types" "UNIT_" (cons '(none) (append ; "apply append" squeezes out nils. (apply append ; create - for each unit (map (lambda (model) (let ((units (model:units model))) (if (null? units) nil (map (lambda (unit) (cons (symbol-append (obj:name model) '- (obj:name unit)) (cons '- (atlist-attrs (obj-atlist model))))) units)))) (current-model-list))) '((max))))) ; FIXME: revisit MAX_UNITS "#define MAX_UNITS (" (number->string (apply max (map (lambda (lengths) (apply max lengths)) (map (lambda (insn) (let ((timing (insn-timing insn))) (if (null? timing) '(1) (map (lambda (insn-timing) (if (null? (cdr insn-timing)) '1 (length (timing:units (cdr insn-timing))))) timing)))) (current-insn-list))))) ")\n\n" ) ) ; Function units. (method-make! 'gen-decl (lambda (self) "")) ; Lookup operand named OP-NAME in INSN. ; Returns #f if OP-NAME is not an operand of INSN. ; IN-OUT is 'in to request an input operand, 'out to request an output operand, ; and 'in-out to request either (though if an operand is used for input and ; output then the input version is returned). ; FIXME: Move elsewhere. (define (insn-op-lookup op-name insn in-out) (letrec ((lookup (lambda (op-list) (cond ((null? op-list) #f) ((eq? op-name (op:sem-name (car op-list))) (car op-list)) (else (lookup (cdr op-list))))))) (case in-out ((in) (lookup (sfmt-in-ops (insn-sfmt insn)))) ((out) (lookup (sfmt-out-ops (insn-sfmt insn)))) ((in-out) (or (lookup (sfmt-in-ops (insn-sfmt insn))) (lookup (sfmt-out-ops (insn-sfmt insn))))) (else (error "insn-op-lookup: bad arg:" in-out)))) ) ; Return C code to profile a unit's usage. ; UNIT-NUM is number of the unit in INSN. ; OVERRIDES is a list of (name value) pairs, where ; - NAME is a spec name, one of cycles, pred, in, out. ; The only ones we're concerned with are in,out. They map operand names ; as they appear in the semantic code to operand names as they appear in ; the function unit spec. ; - VALUE is the operand to NAME. For in,out it is (NAME VALUE) where ; - NAME is the name of an input/output arg of the unit. ; - VALUE is the name of the operand as it appears in semantic code. ; ; ??? This is a big sucker, though half of it is just the definitions ; of utility fns. (method-make! 'gen-profile-code (lambda (self unit-num insn overrides cycles-var-name) (let ( (inputs (unit:inputs self)) (outputs (unit:outputs self)) ; Return C code to initialize UNIT-REFERENCED-VAR to be a bit mask ; of operands of UNIT that were read/written by INSN. ; INSN-REFERENCED-VAR is a bitmask of operands read/written by INSN. ; All we have to do is map INSN-REFERENCED-VAR to ; UNIT-REFERENCED-VAR. ; ??? For now we assume all input operands are read. (gen-ref-arg (lambda (arg num in-out) (let* ((op-name (assq-ref overrides (car arg))) (op (insn-op-lookup (if op-name (car op-name) (car arg)) insn in-out)) (insn-referenced-var "insn_referenced") (unit-referenced-var "referenced")) (if op (if (op:cond? op) (string-append " " "if (" insn-referenced-var " & (1 << " (number->string (op:num op)) ")) " unit-referenced-var " |= 1 << " (number->string num) ";\n") (string-append " " unit-referenced-var " |= 1 << " (number->string num) ";\n")) "")))) ; Initialize unit argument ARG. ; OUT? is #f for input args, #t for output args. (gen-arg-init (lambda (arg out?) (if (or ; Ignore scalars. (null? (cdr arg)) ; Ignore remapped arg, handled elsewhere. (assq (car arg) overrides) ; Ignore operands not in INSN. (not (insn-op-lookup (car arg) insn (if out? 'out 'in)))) "" (let ((sym (gen-profile-sym (gen-c-symbol (car arg)) out?))) (string-append " " sym " = " (gen-argbuf-ref sym) ";\n"))))) ; Return C code to declare variable to hold unit argument ARG. ; OUT? is #f for input args, #t for output args. (gen-arg-decl (lambda (arg out?) (if (null? (cdr arg)) ; ignore scalars "" (string-append " " (mode:c-type (mode:lookup (cadr arg))) " " (gen-profile-sym (gen-c-symbol (car arg)) out?) " = " (if (null? (cddr arg)) "0" (number->string (caddr arg))) ";\n")))) ; Return C code to pass unit argument ARG to the handler. ; OUT? is #f for input args, #t for output args. (gen-arg-arg (lambda (arg out?) (if (null? (cdr arg)) ; ignore scalars "" (string-append ", " (gen-profile-sym (gen-c-symbol (car arg)) out?))))) ) (string-list " {\n" " int referenced = 0;\n" " int UNUSED insn_referenced = abuf->written;\n" ; Declare variables to hold unit arguments. (string-map (lambda (arg) (gen-arg-decl arg #f)) inputs) (string-map (lambda (arg) (gen-arg-decl arg #t)) outputs) ; Initialize 'em, being careful not to initialize an operand that ; has an override. (let (; Make a list of names of in/out overrides. (in-overrides (find-apply cadr (lambda (elm) (eq? (car elm) 'in)) overrides)) (out-overrides (find-apply cadr (lambda (elm) (eq? (car elm) 'out)) overrides))) (string-list (string-map (lambda (arg) (if (memq (car arg) in-overrides) "" (gen-arg-init arg #f))) inputs) (string-map (lambda (arg) (if (memq (car arg) out-overrides) "" (gen-arg-init arg #t))) outputs))) (string-map (lambda (arg) (case (car arg) ((pred) "") ((cycles) "") ((in) (if (caddr arg) (string-append " " (gen-profile-sym (gen-c-symbol (cadr arg)) #f) " = " (gen-argbuf-ref (gen-profile-sym (gen-c-symbol (caddr arg)) #f)) ";\n") "")) ((out) (if (caddr arg) (string-append " " (gen-profile-sym (gen-c-symbol (cadr arg)) #t) " = " (gen-argbuf-ref (gen-profile-sym (gen-c-symbol (caddr arg)) #t)) ";\n") "")) (else (parse-error (make-prefix-context "insn function unit spec") "invalid spec" arg)))) overrides) ; Create bitmask indicating which args were referenced. (string-map (lambda (arg num) (gen-ref-arg arg num 'in)) inputs (iota (length inputs))) (string-map (lambda (arg num) (gen-ref-arg arg num 'out)) outputs (iota (length outputs) (length inputs))) ; Emit the call to the handler. " " cycles-var-name " += " (gen-model-unit-fn-name (unit:model self) self) " (current_cpu, idesc" ", " (number->string unit-num) ", referenced" (string-map (lambda (arg) (gen-arg-arg arg #f)) inputs) (string-map (lambda (arg) (gen-arg-arg arg #t)) outputs) ");\n" " }\n" ))) ) ; Return C code to profile an insn-specific unit's usage. ; UNIT-NUM is number of the unit in INSN. (method-make! 'gen-profile-code (lambda (self unit-num insn cycles-var-name) (let ((args (iunit:args self)) (unit (iunit:unit self))) (send unit 'gen-profile-code unit-num insn args cycles-var-name))) ) ; ARGBUF generation. ; ARGBUF support is put in cpuall.h, which doesn't depend on sim-cpu.scm, ; so this support is here. ; Utility of /gen-argbuf-fields-union to generate the definition for ; SBUF. (define (/gen-argbuf-elm sbuf) (logit 2 "Processing sbuf format " (obj:name sbuf) " ...\n") (string-list " struct { /* " (obj:comment sbuf) " */\n" (let ((elms (sbuf-elms sbuf))) (if (null? elms) " int empty;\n" (string-list-map (lambda (elm) (string-append " " (cadr elm) " " (car elm) ";\n")) (sbuf-elms sbuf)))) " } " (gen-sym sbuf) ";\n") ) ; Utility of gen-argbuf-type to generate the union of extracted ifields. (define (/gen-argbuf-fields-union) (string-list "\ /* Instruction argument buffer. */ union sem_fields {\n" (string-list-map /gen-argbuf-elm (current-sbuf-list)) "\ #if WITH_SCACHE_PBB /* Writeback handler. */ struct { /* Pointer to argbuf entry for insn whose results need writing back. */ const struct argbuf *abuf; } write; /* x-before handler */ struct { /*const SCACHE *insns[MAX_PARALLEL_INSNS];*/ int first_p; } before; /* x-after handler */ struct { int empty; } after; /* This entry is used to terminate each pbb. */ struct { /* Number of insns in pbb. */ int insn_count; /* Next pbb to execute. */ SCACHE *next; SCACHE *branch_target; } chain; #endif };\n\n" ) ) ; Generate the definition of the structure that records arguments. ; This is a union of structures with one structure for each insn format. ; It also includes hardware profiling information and miscellaneous ; administrivia. ; CPU-DATA? is #t if data for the currently selected cpu is to be included. (define (gen-argbuf-type cpu-data?) (logit 2 "Generating ARGBUF type ...\n") (string-list (if (and cpu-data? (with-scache?)) (/gen-argbuf-fields-union) "") (if cpu-data? "" "#ifndef WANT_CPU\n") "\ /* The ARGBUF struct. */ struct argbuf { /* These are the baseclass definitions. */ IADDR addr; const IDESC *idesc; char trace_p; char profile_p; /* ??? Temporary hack for skip insns. */ char skip_count; char unused; /* cpu specific data follows */\n" (if cpu-data? (if (with-scache?) "\ union sem semantic; int written; union sem_fields fields;\n" "\ CGEN_INSN_WORD insn; int written;\n") "") "};\n" (if cpu-data? "" "#endif\n") "\n" ) ) ; Generate the definition of the structure that records a cached insn. ; This is cpu family specific (member `argbuf' is) so it is machine generated. ; CPU-DATA? is #t if data for the currently selected cpu is to be included. (define (gen-scache-type cpu-data?) (logit 2 "Generating SCACHE type ...\n") (string-append (if cpu-data? "" "#ifndef WANT_CPU\n") "\ /* A cached insn. ??? SCACHE used to contain more than just argbuf. We could delete the type entirely and always just use ARGBUF, but for future concerns and as a level of abstraction it is left in. */ struct scache { struct argbuf argbuf;\n" (if (with-generic-write?) "\ int first_insn_p; int last_insn_p;\n" "") "};\n" (if cpu-data? "" "#endif\n") "\n" ) ) ; Mode support. ; Generate a table of mode data. ; For now all we need is the names. (define (gen-mode-defs) (string-append "const char *mode_names[] = {\n" (string-map (lambda (m) (string-append " \"" (string-upcase (obj:str-name m)) "\",\n")) ; We don't treat aliases as being different from the real ; mode here, so ignore them. (mode-list-non-alias-values)) "};\n\n" ) ) ; Insn profiling support. ; Generate declarations for local variables needed for modelling code. (method-make! 'gen-profile-locals (lambda (self model) ; (let ((cti? (or (has-attr? self 'UNCOND-CTI) ; (has-attr? self 'COND-CTI)))) ; (string-append ; (if cti? " int UNUSED taken_p = 0;\n" "") ; )) "") ) ; Generate C code to profile INSN. (method-make! 'gen-profile-code (lambda (self model cycles-var-name) (string-list (let ((timing (assq-ref (insn-timing self) (obj:name model)))) (if timing (string-list-map (lambda (iunit unit-num) (send iunit 'gen-profile-code unit-num self cycles-var-name)) (timing:units timing) (iota (length (timing:units timing)))) (send (model-default-unit model) 'gen-profile-code 0 self nil cycles-var-name))) )) ) ; .cpu file loading support ; Only run sim-analyze-insns! once. (define /sim-insns-analyzed? #f) ; List of computed sformat argument buffers. (define /sim-sformat-abuf-list #f) (define (current-sbuf-list) /sim-sformat-abuf-list) ; Called before/after the .cpu file has been read in. (define (sim-init!) (set! /sim-insns-analyzed? #f) (set! /sim-sformat-abuf-list #f) *UNSPECIFIED* ) ;; Subroutine of /create-virtual-insns!. ;; Add virtual insn INSN to the database. ;; We put virtual insns ahead of normal insns because they're kind of special, ;; and it helps to see them first in lists. ;; ORDINAL is a used to place the insn ahead of normal insns; ;; it is a pair so we can do the update for the next virtual insn here. (define (/virtual-insn-add! ordinal insn) (obj-set-ordinal! insn (cdr ordinal)) (current-insn-add! insn) (set-cdr! ordinal (- (cdr ordinal) 1)) ) ; Create the virtual insns. (define (/create-virtual-insns!) (let ((all (all-isas-attr-value)) (context (make-prefix-context "virtual insns")) ;; Record as a pair so /virtual-insn-add! can update it. (ordinal (cons #f -1))) (/virtual-insn-add! ordinal (insn-read context '(name x-begin) '(comment "pbb begin handler") `(attrs VIRTUAL PBB (ISA ,@all)) '(syntax "--begin--") '(semantics (c-code VOID "\ { #if WITH_SCACHE_PBB_@PREFIX@ #if defined DEFINE_SWITCH || defined FAST_P /* In the switch case FAST_P is a constant, allowing several optimizations in any called inline functions. */ vpc = @prefix@_pbb_begin (current_cpu, FAST_P); #else #if 0 /* cgen engine can't handle dynamic fast/full switching yet. */ vpc = @prefix@_pbb_begin (current_cpu, STATE_RUN_FAST_P (CPU_STATE (current_cpu))); #else vpc = @prefix@_pbb_begin (current_cpu, 0); #endif #endif #endif } ")) )) (/virtual-insn-add! ordinal (insn-read context '(name x-chain) '(comment "pbb chain handler") `(attrs VIRTUAL PBB (ISA ,@all)) '(syntax "--chain--") '(semantics (c-code VOID "\ { #if WITH_SCACHE_PBB_@PREFIX@ vpc = @prefix@_pbb_chain (current_cpu, sem_arg); #ifdef DEFINE_SWITCH BREAK (sem); #endif #endif } ")) )) (/virtual-insn-add! ordinal (insn-read context '(name x-cti-chain) '(comment "pbb cti-chain handler") `(attrs VIRTUAL PBB (ISA ,@all)) '(syntax "--cti-chain--") '(semantics (c-code VOID "\ { #if WITH_SCACHE_PBB_@PREFIX@ #ifdef DEFINE_SWITCH vpc = @prefix@_pbb_cti_chain (current_cpu, sem_arg, pbb_br_type, pbb_br_npc); BREAK (sem); #else /* FIXME: Allow provision of explicit ifmt spec in insn spec. */ vpc = @prefix@_pbb_cti_chain (current_cpu, sem_arg, CPU_PBB_BR_TYPE (current_cpu), CPU_PBB_BR_NPC (current_cpu)); #endif #endif } ")) )) (/virtual-insn-add! ordinal (insn-read context '(name x-before) '(comment "pbb begin handler") `(attrs VIRTUAL PBB (ISA ,@all)) '(syntax "--before--") '(semantics (c-code VOID "\ { #if WITH_SCACHE_PBB_@PREFIX@ @prefix@_pbb_before (current_cpu, sem_arg); #endif } ")) )) (/virtual-insn-add! ordinal (insn-read context '(name x-after) '(comment "pbb after handler") `(attrs VIRTUAL PBB (ISA ,@all)) '(syntax "--after--") '(semantics (c-code VOID "\ { #if WITH_SCACHE_PBB_@PREFIX@ @prefix@_pbb_after (current_cpu, sem_arg); #endif } ")) )) (/virtual-insn-add! ordinal (insn-read context '(name x-invalid) '(comment "invalid insn handler") `(attrs VIRTUAL (ISA ,@all)) '(syntax "--invalid--") (list 'semantics (list 'c-code 'VOID (string-append "\ { /* Update the recorded pc in the cpu state struct. Only necessary for WITH_SCACHE case, but to avoid the conditional compilation .... */ SET_H_PC (pc); /* Virtual insns have zero size. Overwrite vpc with address of next insn using the default-insn-bitsize spec. When executing insns in parallel we may want to queue the fault and continue execution. */ vpc = SEM_NEXT_VPC (sem_arg, pc, " (number->string (bits->bytes (state-default-insn-bitsize))) "); vpc = sim_engine_invalid_insn (current_cpu, pc, vpc); } "))) )) ) ) (define (sim-finish!) ; Add begin,chain,before,after,invalid handlers if not provided. ; The code generators should first look for x-foo-@prefix@, then for x-foo. ; ??? This is good enough for the first pass. Will eventually need to use ; less C and more RTL. (/create-virtual-insns!) *UNSPECIFIED* ) ; Called after file is read in and global error checks are done ; to initialize tables. (define (sim-analyze!) *UNSPECIFIED* ) ; Scan insns, analyzing semantics and computing instruction formats. ; 'twould be nice to do this in sim-analyze! but it doesn't know whether this ; needs to be done or not (which is determined by what files are being ; generated). Since this is an expensive operation, we defer doing this ; to the files that need it. (define (sim-analyze-insns!) ; This can only be done if one isa and one cpu family is being kept. (assert-keep-one) (if (not /sim-insns-analyzed?) (begin (arch-analyze-insns! CURRENT-ARCH #f ; don't include aliases #t) ; do analyze the semantics ; Compute the set of sformat argument buffers. (set! /sim-sformat-abuf-list (compute-sformat-argbufs! (current-sfmt-list))) (set! /sim-insns-analyzed? #t))) ; Do our own error checking. (assert (current-insn-lookup 'x-invalid #f)) *UNSPECIFIED* ) ; For debugging. (define (cgen-all-arch) (string-write cgen-arch.h cgen-arch.c cgen-cpuall.h ;cgen-mem-ops.h ;cgen-sem-ops.h ;cgen-ops.c ) ) (define (cgen-all-cpu) (string-write cgen-cpu.h cgen-cpu.c cgen-decode.h cgen-decode.c ;cgen-extract.c cgen-read.c cgen-write.c cgen-semantics.c cgen-sem-switch.c cgen-model.c ;cgen-mainloop.in ) )