; CPU family related simulator generator, excluding decoding and model support. ; Copyright (C) 2000, 2002, 2003, 2005, 2006, 2009 Red Hat, Inc. ; This file is part of CGEN. ; *********** ; cgen-desc.h (define (/last-insn) (string-upcase (gen-c-symbol (caar (list-take -1 (gen-obj-list-enums (non-multi-insns (current-insn-list)))))))) ; Declare the attributes. (define (/gen-attr-decls) (string-list "// Insn attribute indices.\n\n" (gen-attr-enum-decl "cgen_insn" (current-insn-attr-list)) "// Attributes.\n\n" (string-list-map gen-decl (current-attr-list)) ) ) ; Generate class to hold an instruction's attributes. (define (/gen-insn-attr-decls) (let ((attrs (current-insn-attr-list))) (string-append "// Insn attributes.\n\n" ; FIXME: maybe make class, but that'll require a constructor. Later. "struct @arch@_insn_attr {\n" " unsigned int bools;\n" (string-map (lambda (attr) (if (bool-attr? attr) "" (string-append " " (gen-attr-type attr) " " (string-downcase (gen-sym attr)) ";\n"))) attrs) ;"public:\n" (string-map (lambda (attr) (string-append " inline " (gen-attr-type attr) " get_" (string-downcase (gen-sym attr)) "_attr" " () { return " (if (bool-attr? attr) (string-append "(bools & " (gen-attr-mask "cgen_insn" (obj:name attr)) ") != 0") (string-downcase (gen-sym attr))) "; }\n")) attrs) "};\n\n" )) ) ; Emit a macro that specifies the word-bitsize for each machine. (define (/gen-mach-params) (string-map (lambda (mach) (string-append "#define MACH_" (string-upcase (gen-sym mach)) "_INSN_CHUNK_BITSIZE " (number->string (cpu-insn-chunk-bitsize (mach-cpu mach))) "\n")) (current-mach-list)) ) ; Generate -desc.h. (define (cgen-desc.h) (logit 1 "Generating " (gen-cpu-name) "-desc.h ...\n") (string-write (gen-c-copyright "Misc. entries in the @arch@ description file." copyright-red-hat package-red-hat-simulators) "\ #ifndef DESC_@ARCH@_H #define DESC_@ARCH@_H #include \"cgen/bitset.h\" namespace @arch@ { \n" "// Enums.\n\n" (lambda () (string-map gen-decl (current-enum-list))) /gen-attr-decls /gen-insn-attr-decls /gen-mach-params " } // end @arch@ namespace #endif /* DESC_@ARCH@_H */\n" ) ) ; ********** ; cgen-cpu.h ; Print out file containing elements to add to cpu class. ; Get/set fns for hardware element HW. (define (/gen-reg-access-defns hw) (let ((scalar? (hw-scalar? hw)) (name (obj:name hw)) (getter (hw-getter hw)) (setter (hw-setter hw)) (type (gen-type hw))) (let ((get-code (if getter (let ((mode (hw-mode hw)) (args (car getter)) (expr (cadr getter))) (string-append "return " (rtl-c++ mode #f ;; h/w is not ISA-specific (if scalar? nil (list (list (car args) 'UINT "regno"))) expr #:rtl-cover-fns? #t) ";")) (string-append "return this->hardware." (gen-c-symbol name) (if scalar? "" "[regno]") ";"))) (set-code (if setter (let ((args (car setter)) (expr (cadr setter))) (rtl-c++ VOID ; not `mode', sets have mode VOID #f ;; h/w is not ISA-specific (if scalar? (list (list (car args) (hw-mode hw) "newval")) (list (list (car args) 'UINT "regno") (list (cadr args) (hw-mode hw) "newval"))) expr #:rtl-cover-fns? #t)) (string-append "this->hardware." (gen-c-symbol name) (if scalar? "" "[regno]") " = newval;")))) (string-append " inline " type " " (gen-reg-get-fun-name hw) " (" (if scalar? "" "UINT regno") ") const" " { " get-code " }" "\n" " inline void " (gen-reg-set-fun-name hw) " (" (if scalar? "" "UINT regno, ") type " newval)" " { " set-code " }" "\n\n"))) ) ; Return a boolean indicating if hardware element HW needs storage allocated ; for it in the SIM_CPU struct. (define (hw-need-storage? hw) (and (register? hw) (not (obj-has-attr? hw 'VIRTUAL))) ) (define (hw-need-write-stack? hw) (and (register? hw) (hw-used-in-delay-rtl? hw)) ) ; Subroutine of /gen-hardware-types to generate the struct containing ; hardware elements of one isa. (define (/gen-hardware-struct prefix hw-list) (if (null? hw-list) ; If struct is empty, leave it out to simplify generated code. "" (string-list (if prefix (string-append " // Hardware elements for " prefix ".\n") " // Hardware elements.\n") " struct {\n" (string-list-map gen-defn hw-list) " } " (if prefix (string-append prefix "_") "") "hardware;\n\n" )) ) ; Return C type declarations of all of the hardware elements. ; The name of the type is prepended with the cpu family name. (define (/gen-hardware-types) (string-list "// CPU state information.\n\n" (/gen-hardware-struct #f (find hw-need-storage? (current-hw-list)))) ) (define (/gen-hw-stream-and-destream-fns) (let* ((sa string-append) (regs (find hw-need-storage? (current-hw-list))) (stack-regs (find hw-need-write-stack? (current-hw-list))) (reg-dim (lambda (r) (let ((dims (/hw-vector-dims r))) (if (equal? 0 (length dims)) "0" (number->string (car dims)))))) (write-stacks (map (lambda (n) (sa n "_writes")) (append (map (lambda (r) (gen-c-symbol (obj:name r))) stack-regs) (map (lambda (m) (sa (symbol->string m) "_memory")) write-stack-memory-mode-names)))) (stream-reg (lambda (r) (let ((rname (sa "hardware." (gen-c-symbol (obj:name r))))) (if (hw-scalar? r) (sa " ost << " rname " << ' ';\n") (sa " for (int i = 0; i < " (reg-dim r) "; i++)\n ost << " rname "[i] << ' ';\n"))))) (destream-reg (lambda (r) (let ((rname (sa "hardware." (gen-c-symbol (obj:name r))))) (if (hw-scalar? r) (sa " ist >> " rname ";\n") (sa " for (int i = 0; i < " (reg-dim r) "; i++)\n ist >> " rname "[i];\n"))))) (stream-stacks (lambda (s) (sa " stream_stacks ( stacks." s ", ost);\n"))) (destream-stacks (lambda (s) (sa " destream_stacks ( stacks." s ", ist);\n"))) (stack-boilerplate (sa " template \n" " void stream_stacks (const ST &st, std::ostream &ost) const\n" " {\n" " for (int i = 0; i < @prefix@::pipe_sz; i++)\n" " {\n" " ost << st[i].t << ' ';\n" " for (int j = 0; j <= st[i].t; j++)\n" " {\n" " ost << st[i].buf[j].pc << ' ';\n" " ost << st[i].buf[j].val << ' ';\n" " ost << st[i].buf[j].idx0 << ' ';\n" " }\n" " }\n" " }\n" " \n" " template \n" " void destream_stacks (ST &st, std::istream &ist)\n" " {\n" " for (int i = 0; i < @prefix@::pipe_sz; i++)\n" " {\n" " ist >> st[i].t;\n" " for (int j = 0; j <= st[i].t; j++)\n" " {\n" " ist >> st[i].buf[j].pc;\n" " ist >> st[i].buf[j].val;\n" " ist >> st[i].buf[j].idx0;\n" " }\n" " }\n" " }\n" " \n"))) (sa " void stream_cgen_hardware (std::ostream &ost) const \n {\n" (string-map stream-reg regs) " }\n" " void destream_cgen_hardware (std::istream &ist) \n {\n" (string-map destream-reg regs) " }\n" (if (with-parallel?) (sa stack-boilerplate " void stream_cgen_write_stacks (std::ostream &ost, " "const @prefix@::write_stacks &stacks) const \n {\n" (string-map stream-stacks write-stacks) " }\n" " void destream_cgen_write_stacks (std::istream &ist, " "@prefix@::write_stacks &stacks) \n {\n" (string-map destream-stacks write-stacks) " }\n") "")))) ; Generate -cpu.h (define (cgen-cpu.h) (logit 1 "Generating " (gen-cpu-name) "-cpu.h ...\n") (assert-keep-one) ; Turn parallel execution support on if cpu needs it. (set-with-parallel?! (state-parallel-exec?)) ; Initialize rtl->c generation. (rtl-c-config! #:rtl-cover-fns? #t) (string-write (gen-c-copyright "CPU class elements for @cpu@." copyright-red-hat package-red-hat-simulators) "\ // This file is included in the middle of the cpu class struct. public: \n" /gen-hardware-types /gen-hw-stream-and-destream-fns " // C++ register access function templates\n" "#define current_cpu this\n\n" (lambda () (string-list-map /gen-reg-access-defns (find register? (current-hw-list)))) "#undef current_cpu\n\n" ) ) ; ********** ; cgen-defs.h ; Print various parameters of the cpu family. ; A "cpu family" here is a collection of variants of a particular architecture ; that share sufficient commonality that they can be handled together. (define (/gen-cpu-defines) (string-append "\ /* Maximum number of instructions that are fetched at a time. This is for LIW type instructions sets (e.g. m32r). */\n" "#define @CPU@_MAX_LIW_INSNS " (number->string (cpu-liw-insns (current-cpu))) "\n\n" "/* Maximum number of instructions that can be executed in parallel. */\n" "#define @CPU@_MAX_PARALLEL_INSNS " (number->string (cpu-parallel-insns (current-cpu))) "\n" "\n" ; (gen-enum-decl '@prefix@_virtual ; "@prefix@ virtual insns" ; "@ARCH@_INSN_" ; not @CPU@ to match CGEN_INSN_TYPE in opc.h ; '((x-invalid 0) ; (x-before -1) (x-after -2) ; (x-begin -3) (x-chain -4) (x-cti-chain -5))) ) ) ; Generate type of struct holding model state while executing. (define (/gen-model-decls) (logit 2 "Generating model decls ...\n") (string-list (string-list-map (lambda (model) (string-list "typedef struct {\n" (if (null? (model:state model)) " int empty;\n" (string-map (lambda (var) (string-append " " (mode:c-type (mode:lookup (cadr var))) " " (gen-c-symbol (car var)) ";\n")) (model:state model))) "} " (if (null? (model:state model)) "BLANK" "@CPU@") "_MODEL_DATA;\n\n" )) (current-model-list)) " typedef int (@CPU@_MODEL_FN) (struct @cpu@_cpu*, void*); typedef struct { /* This is an integer that identifies this insn. How this works is up to the target. */ int num; /* Function to handle insn-specific profiling. */ @CPU@_MODEL_FN *model_fn; /* Array of function units used by this insn. */ UNIT units[MAX_UNITS]; } @CPU@_INSN_TIMING;" ) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; begin stack-based write schedule ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define write-stack-memory-mode-names '()) (define (/calculated-memory-write-buffer-size) (let* ((is-mem? (lambda (op) (eq? (hw-sem-name (op:type op)) 'h-memory))) (count-mem-writes (lambda (sfmt) (length (find is-mem? (sfmt-out-ops sfmt)))))) (apply max (append '(0) (map count-mem-writes (current-sfmt-list)))))) ;; note: this doesn't really correctly approximate the worst case. user-supplied functions ;; might rewrite the pipeline extensively while it's running. ;(define (/worst-case-number-of-writes-to hw-name) ; (let* ((sfmts (current-sfmt-list)) ; (out-ops (map sfmt-out-ops sfmts)) ; (pred (lambda (op) (equal? hw-name (gen-c-symbol (obj:name (op:type op)))))) ; (filtered-ops (map (lambda (ops) (find pred ops)) out-ops))) ; (apply max (cons 0 (map (lambda (ops) (length ops)) filtered-ops))))) (define (/hw-gen-write-stack-decl nm mode) (let* ( ; for the time being, we're disabling this size-estimation stuff and just ; requiring the user to supply a parameter WRITE_BUF_SZ before they include -defs.h ; (pipe-sz (+ 1 (max-delay (cpu-max-delay (current-cpu))))) ; (sz (* pipe-sz (/worst-case-number-of-writes-to nm)))) (mode-pad (spaces (- 4 (string-length (symbol->string mode))))) (stack-name (string-append nm "_writes"))) (string-append " write_stack< write<" (symbol->string mode) "> >" mode-pad "\t" stack-name "\t[pipe_sz];\n"))) (define (/hw-gen-write-struct-decl) (let* ((dims (/worst-case-index-dims)) (sa string-append) (ns number->string) (idxs (iota dims)) (ctor (sa "write (PCADDR _pc, MODE _val" (string-map (lambda (x) (sa ", USI _idx" (ns x) "=0")) idxs) ") : pc(_pc), val(_val)" (string-map (lambda (x) (sa ", idx" (ns x) "(_idx" (ns x) ")")) idxs) " {} \n")) (idx-fields (string-map (lambda (x) (sa " USI idx" (ns x) ";\n")) idxs))) (sa "\n\n" " template \n" " struct write\n" " {\n" " USI pc;\n" " MODE val;\n" idx-fields " " ctor " write() {}\n" " };\n" ))) (define (/hw-vector-dims hw) (elm-get (hw-type hw) 'dimensions)) (define (/worst-case-index-dims) (apply max (append '(1) ; for memory accesses (map (lambda (hw) (length (/hw-vector-dims hw))) (find (lambda (hw) (not (scalar? hw))) (current-hw-list)))))) (define (/gen-writestacks) (let* ((hw (find hw-need-write-stack? (current-hw-list))) (modes write-stack-memory-mode-names) (hw-pairs (map (lambda (h) (list (gen-c-symbol (obj:name h)) (obj:name (hw-mode h)))) hw)) (mem-pairs (map (lambda (m) (list (string-append (symbol->string m) "_memory") m)) modes)) (all-pairs (append mem-pairs hw-pairs)) (h1 "\n\n// write stacks used in parallel execution\n\n struct write_stacks\n {\n // types of stacks\n\n") (wb (string-append "\n\n // unified writeback function (defined in @prefix@-write.cc)" "\n void writeback (int tick, @cpu@::@cpu@_cpu* current_cpu);" "\n // unified write-stack clearing function (defined in @prefix@-write.cc)" "\n void reset ();")) (zz "\n\n }; // end struct @prefix@::write_stacks \n\n")) (string-append (/hw-gen-write-struct-decl) (foldl (lambda (s pair) (string-append s (apply /hw-gen-write-stack-decl pair))) h1 all-pairs) wb zz))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; end stack-based write schedule ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Generate the definition of the structure that holds register values, etc. ; for use during parallel execution. (define (gen-write-stack-structure) (let ((membuf-sz (/calculated-memory-write-buffer-size)) (max-delay (cpu-max-delay (current-cpu)))) (logit 2 "Generating write stack structure ...\n") (string-append " static const int max_delay = " (number->string max-delay) ";\n" " static const int pipe_sz = " (number->string (+ 1 max-delay)) "; // max_delay + 1\n" " template struct write_stack { int t; const int sz; ELT buf[WRITE_BUF_SZ]; write_stack () : t(-1), sz(WRITE_BUF_SZ) {} inline bool empty () { return (t == -1); } inline void clear () { t = -1; } inline void pop () { if (t > -1) t--;} inline void push (const ELT &e) { if (t+1 < sz) buf [++t] = e;} inline ELT &top () { return buf [t>0 ? ( t inline VAL lookahead (int dist, int base, STKS &st, VAL def, int idx=0) { for (; dist > 0; --dist) { write_stack &v = st [(base + dist) % pipe_sz]; for (int i = v.t; i > 0; --i) if (v.buf [i].idx0 == idx) return v.buf [i]; } return def; } " (/gen-writestacks) ))) ; Generate the TRACE_RECORD struct definition. (define (/gen-trace-record-type) (string-list "\ /* Collection of various things for the trace handler to use. */ typedef struct @prefix@_trace_record { PCADDR pc; /* FIXME:wip */ } @CPU@_TRACE_RECORD; \n" ) ) ; Generate -defs.h (define (cgen-defs.h) (logit 1 "Generating " (gen-cpu-name) "-defs.h ...\n") (assert-keep-one) ; Turn parallel execution support on if cpu needs it. (set-with-parallel?! (state-parallel-exec?)) ; Initialize rtl->c generation. (rtl-c-config! #:rtl-cover-fns? #t) (string-write (gen-c-copyright "CPU family header for @cpu@ / @prefix@." copyright-red-hat package-red-hat-simulators) "\ #ifndef DEFS_@PREFIX@_H #define DEFS_@PREFIX@_H ") (if (with-parallel?) (string-write "\ #include #include \"cgen-types.h\" // forward declaration\n\n namespace @cpu@ { struct @cpu@_cpu; } namespace @prefix@ { using namespace cgen; " gen-write-stack-structure "\ } // end @prefix@ namespace ")) (string-write "\ #endif /* DEFS_@PREFIX@_H */\n" ) ) ; ************** ; cgen-write.cxx ; This is the other way of implementing parallel execution support. ; Instead of fetching all the input operands first, write all the output ; operands and their addresses to holding variables, and then run a ; post-processing pass to update the cpu state. ; Return C code to fetch and save all output operands to instructions with ; SFMT. ; Generate -write.cxx. (define (/gen-register-writer nm mode dims) (let* ((pad " ") (sa string-append) (mode (symbol->string mode)) (idx-args (string-map (lambda (x) (sa "w.idx" (number->string x) ", ")) (iota dims)))) (sa pad "while (! " nm "_writes[tick].empty())\n" pad "{\n" pad " write<" mode "> &w = " nm "_writes[tick].top();\n" pad " current_cpu->" nm "_set(" idx-args "w.val);\n" pad " " nm "_writes[tick].pop();\n" pad "}\n\n"))) (define (/gen-memory-writer nm mode dims) (let* ((pad " ") (sa string-append) (mode (symbol->string mode)) (idx-args (string-map (lambda (x) (sa ", w.idx" (number->string x) "")) (iota dims)))) (sa pad "while (! " nm "_writes[tick].empty())\n" pad "{\n" pad " write<" mode "> &w = " nm "_writes[tick].top();\n" pad " current_cpu->SETMEM" mode " (w.pc" idx-args ", w.val);\n" pad " " nm "_writes[tick].pop();\n" pad "}\n\n"))) (define (/gen-reset-fn) (let* ((sa string-append) (objs (append (map (lambda (h) (gen-c-symbol (obj:name h))) (find hw-need-write-stack? (current-hw-list))) (map (lambda (m) (sa (symbol->string m) "_memory")) write-stack-memory-mode-names))) (clr (lambda (elt) (sa " clear_stacks (" elt "_writes);\n")))) (sa " template \n" " static void clear_stacks (ST &st)\n" " {\n" " for (int i = 0; i < @prefix@::pipe_sz; i++)\n" " st[i].clear();\n" " }\n\n" " void @prefix@::write_stacks::reset ()\n {\n" (string-map clr objs) " }"))) (define (/gen-unified-write-fn) (let* ((hw (find hw-need-write-stack? (current-hw-list))) (modes write-stack-memory-mode-names) (hw-triples (map (lambda (h) (list (gen-c-symbol (obj:name h)) (obj:name (hw-mode h)) (length (/hw-vector-dims h)))) hw)) (mem-triples (map (lambda (m) (list (string-append (symbol->string m) "_memory") m 1)) modes))) (logit 2 "Generating writer function ...\n") (string-append " void @prefix@::write_stacks::writeback (int tick, @cpu@::@cpu@_cpu* current_cpu) { " "\n // register writeback loops\n" (string-map (lambda (t) (apply /gen-register-writer t)) hw-triples) "\n // memory writeback loops\n" (string-map (lambda (t) (apply /gen-memory-writer t)) mem-triples) " } "))) (define (cgen-write.cxx) (logit 1 "Generating " (gen-cpu-name) "-write.cxx ...\n") (assert-keep-one) (sim-analyze-insns!) ; Turn parallel execution support off. (set-with-parallel?! #f) ; Tell the rtx->c translator we are the simulator. (rtl-c-config! #:rtl-cover-fns? #t) (string-write (gen-c-copyright (string-append "Simulator instruction operand writer for " (symbol->string (current-arch-name)) ".") copyright-red-hat package-red-hat-simulators) "\ #include \"@cpu@.h\" " /gen-reset-fn /gen-unified-write-fn ) ) ; ****************** ; cgen-semantics.cxx ; Return C code to perform the semantics of INSN. (define (gen-semantic-code insn) (cond ((insn-compiled-semantics insn) => (lambda (sem) (rtl-c++-parsed VOID sem #:for-insn? #t #:rtl-cover-fns? #t #:owner insn))) ((insn-canonical-semantics insn) => (lambda (sem) (rtl-c++-parsed VOID sem #:for-insn? #t #:rtl-cover-fns? #t #:owner insn))) (else (context-error (make-obj-context insn #f) "While generating semantic code" "semantics of insn are not canonicalized"))) ) ; Return definition of C function to perform INSN. ; This version handles the with-scache case. (define (/gen-scache-semantic-fn insn) (logit 2 "Processing semantics for " (obj:name insn) ": \"" (insn-syntax insn) "\" ...\n") (set! /with-profile? /with-profile-fn?) (let ((cti? (insn-cti? insn)) (insn-len (insn-length-bytes insn))) (string-list "// ********** " (obj:name insn) ": " (insn-syntax insn) "\n\n" (if (with-parallel?) "void\n" "sem_status\n") "@prefix@_sem_" (gen-sym insn) (if (with-parallel?) (string-append " (@cpu@_cpu* current_cpu, @prefix@_scache* sem, const int tick, \n\t" "@prefix@::write_stacks &buf)\n") " (@cpu@_cpu* current_cpu, @prefix@_scache* sem)\n") "{\n" (gen-define-field-macro (insn-sfmt insn)) " sem_status status = SEM_STATUS_NORMAL;\n" " @prefix@_scache* abuf = sem;\n" ; Unconditionally written operands are not recorded here. (if (or (with-profile?) (with-parallel-write?)) " unsigned long long written = 0;\n" "") ; The address of this insn, needed by extraction and semantic code. ; Note that the address recorded in the cpu state struct is not used. ; For faster engines that copy will be out of date. " PCADDR pc = abuf->addr;\n" " PCADDR npc = pc + " (number->string insn-len) ";\n" "\n" (gen-semantic-code insn) "\n" ; Only update what's been written if some are conditionally written. ; Otherwise we know they're all written so there's no point in ; keeping track. (if (or (with-profile?) (with-parallel-write?)) (if (/any-cond-written? (insn-sfmt insn)) " abuf->written = written;\n" "") "") (if cti? " current_cpu->done_cti_insn (npc, status);\n" " current_cpu->done_insn (npc, status);\n") (if (with-parallel?) "" " return status;\n") (gen-undef-field-macro (insn-sfmt insn)) "}\n\n" )) ) (define (/gen-all-semantic-fns) (logit 2 "Processing semantics ...\n") (let ((insns (scache-engine-insns))) (if (with-scache?) (string-write-map /gen-scache-semantic-fn insns) (error "must specify `with-scache'"))) ) ; Generate -sem.cxx. ; Each instruction is implemented in its own function. (define (cgen-semantics.cxx) (logit 1 "Generating " (gen-cpu-name) "-semantics.cxx ...\n") (assert-keep-one) (sim-analyze-insns!) ; Turn parallel execution support on if cpu needs it. (set-with-parallel?! (state-parallel-exec?)) ; Tell the rtx->c translator we are the simulator. (rtl-c-config! #:rtl-cover-fns? #t) ; Indicate we're currently not generating a pbb engine. (set-current-pbb-engine?! #f) (string-write (gen-c-copyright "Simulator instruction semantics for @prefix@." copyright-red-hat package-red-hat-simulators) "\ #if HAVE_CONFIG_H #include \"config.h\" #endif #include \"@cpu@.h\" using namespace @cpu@; // FIXME: namespace organization still wip\n") (if (with-parallel?) (string-write "\ using namespace @prefix@; // FIXME: namespace organization still wip\n")) (string-write "\ #define GET_ATTR(name) GET_ATTR_##name () \n" /gen-all-semantic-fns ) ) ; ******************* ; cgen-sem-switch.cxx ; ; The semantic switch engine has two flavors: one case per insn, and one ; case per "frag" (where each insn is split into one or more fragments). ; Utility of /gen-sem-case to return the mask of operands always written ; to in SFMT. ; ??? Not currently used. (define (/uncond-written-mask sfmt) (apply + (map (lambda (op) (if (op:cond? op) 0 (logsll 1 (op:num op)))) (sfmt-out-ops sfmt))) ) ; Utility of /gen-sem-case to return #t if any operand in SFMT is ; conditionally written to. (define (/any-cond-written? sfmt) (any-true? (map op:cond? (sfmt-out-ops sfmt))) ) ; One case per insn version. ; Generate a switch case to perform INSN. (define (/gen-sem-case insn parallel?) (logit 2 "Processing " (if parallel? "parallel " "") "semantic switch case for " (obj:name insn) ": \"" (insn-syntax insn) "\" ...\n") (set! /with-profile? /with-profile-sw?) (let ((cti? (insn-cti? insn)) (insn-len (insn-length-bytes insn))) (string-list ; INSN_ is prepended here and not elsewhere to avoid name collisions ; with symbols like AND, etc. "\ // ********** " (insn-syntax insn) " CASE (INSN_" (if parallel? "PAR_" "") (string-upcase (gen-sym insn)) "): { @prefix@_scache* abuf = vpc;\n" (if (with-scache?) (gen-define-field-macro (insn-sfmt insn)) "") ; Unconditionally written operands are not recorded here. (if (or (with-profile?) (with-parallel-write?)) " unsigned long long written = 0;\n" "") ; The address of this insn, needed by extraction and semantic code. ; Note that the address recorded in the cpu state struct is not used. " PCADDR pc = abuf->addr;\n" (if (and cti? (not parallel?)) (string-append " PCADDR npc;\n" " branch_status br_status = BRANCH_UNTAKEN;\n") "") (string-list " vpc = vpc + 1;\n") ; Emit setup-semantics code for real insns. (if (and (insn-real? insn) (isa-setup-semantics (current-isa))) (string-append " " (rtl-c++ VOID (obj-isa-list insn) nil (isa-setup-semantics (current-isa)) #:for-insn? #t #:rtl-cover-fns? #t #:owner insn)) "") "\n" (gen-semantic-code insn) "\n" ; Only update what's been written if some are conditionally written. ; Otherwise we know they're all written so there's no point in ; keeping track. (if (or (with-profile?) (with-parallel-write?)) (if (/any-cond-written? (insn-sfmt insn)) " abuf->written = written;\n" "") "") (if (and cti? (not parallel?)) (string-append " pbb_br_npc = npc;\n" " pbb_br_status = br_status;\n") "") (if (with-scache?) (gen-undef-field-macro (insn-sfmt insn)) "") " }\n" " NEXT (vpc);\n\n" )) ) (define (/gen-sem-switch) (logit 2 "Processing semantic switch ...\n") ; Turn parallel execution support off. (set-with-parallel?! #f) (string-write-map (lambda (insn) (/gen-sem-case insn #f)) (non-multi-insns (non-alias-insns (current-insn-list)))) ) ; Generate the guts of a C switch statement to execute parallel instructions. ; This switch is included after the non-parallel instructions in the semantic ; switch. ; ; ??? We duplicate the writeback case for each insn, even though we only need ; one case per insn format. The former keeps the code for each insn ; together and might improve cache usage. On the other hand the latter ; reduces the amount of code, though it is believed that in this particular ; instance the win isn't big enough. (define (/gen-parallel-sem-switch) (logit 2 "Processing parallel insn semantic switch ...\n") ; Turn parallel execution support on. (set-with-parallel?! #t) (string-write-map (lambda (insn) (string-list (/gen-sem-case insn #t) (/gen-write-case (insn-sfmt insn) insn))) (parallel-insns (current-insn-list))) ) ; Return computed-goto engine. (define (/gen-sem-switch-engine) (string-write "\ void @cpu@_cpu::@prefix@_pbb_run () { @cpu@_cpu* current_cpu = this; @prefix@_scache* vpc; // These two are used to pass data from cti insns to the cti-chain insn. PCADDR pbb_br_npc; branch_status pbb_br_status; #ifdef __GNUC__ { static const struct sem_labels { enum @prefix@_insn_type insn; void *label; } labels[] = {\n" (lambda () (string-write-map (lambda (insn) (string-append " { " "@PREFIX@_INSN_" (string-upcase (gen-sym insn)) ", && case_INSN_" (string-upcase (gen-sym insn)) " },\n")) (non-multi-insns (non-alias-insns (current-insn-list))))) (if (state-parallel-exec?) (lambda () (string-write-map (lambda (insn) (string-append " { " "@PREFIX@_INSN_PAR_" (string-upcase (gen-sym insn)) ", && case_INSN_PAR_" (string-upcase (gen-sym insn)) " },\n" " { " "@PREFIX@_INSN_WRITE_" (string-upcase (gen-sym insn)) ", && case_INSN_WRITE_" (string-upcase (gen-sym insn)) " },\n")) (parallel-insns (current-insn-list)))) "") " { (@prefix@_insn_type) 0, 0 } }; if (! @prefix@_idesc::idesc_table_initialized_p) { for (int i=0; labels[i].label != 0; i++) @prefix@_idesc::idesc_table[labels[i].insn].cgoto.label = labels[i].label; // confirm that table is all filled up for (int i = 0; i <= @PREFIX@_INSN_" (/last-insn) "; i++) assert (@prefix@_idesc::idesc_table[i].cgoto.label != 0); // Initialize the compiler virtual insn. current_cpu->@prefix@_engine.compile_begin_insn (current_cpu); @prefix@_idesc::idesc_table_initialized_p = true; } } #endif #ifdef __GNUC__ #define CASE(X) case_##X // Branch to next handler without going around main loop. #define NEXT(vpc) goto * vpc->execute.cgoto.label; // Break out of threaded interpreter and return to \"main loop\". #define BREAK(vpc) goto end_switch #else #define CASE(X) case @PREFIX@_##X #define NEXT(vpc) goto restart #define BREAK(vpc) break #endif // Get next insn to execute. vpc = current_cpu->@prefix@_engine.get_next_vpc (current_cpu->h_pc_get ()); restart: #ifdef __GNUC__ goto * vpc->execute.cgoto.label; #else switch (vpc->idesc->sem_index) #endif { " /gen-sem-switch (if (state-parallel-exec?) /gen-parallel-sem-switch "") " #ifdef __GNUC__ end_switch: ; #else default: abort(); #endif } // Save vpc for next time. current_cpu->@prefix@_engine.set_next_vpc (vpc); } \n" ) ) ; Semantic frag version. ; Return declaration of frag enum. (define (/gen-sfrag-enum-decl frag-list) (gen-enum-decl "@prefix@_frag_type" "semantic fragments in cpu family @prefix@" "@PREFIX@_FRAG_" (append '((list-end)) (map (lambda (i) (cons (obj:name i) (cons '- (atlist-attrs (obj-atlist i))))) frag-list) '((max)))) ) ; Return header file decls for semantic frag threaded engine. (define (/gen-sfrag-engine-decls) (string-write "namespace @cpu@ {\n\n" ; FIXME: vector->list (/gen-sfrag-enum-decl (vector->list (sim-sfrag-frag-table))) "\ struct @prefix@_insn_frag { @PREFIX@_INSN_TYPE itype; // 4: header+middle+trailer+delimiter @PREFIX@_FRAG_TYPE ftype[4]; }; struct @prefix@_pbb_label { @PREFIX@_FRAG_TYPE frag; void *label; }; } // end @cpu@ namespace \n") ) ; Return C code to perform the semantics of FRAG. ; LOCALS is a list of sequence locals made global to all frags. ; Each element is (symbol "c-var-name"). (define (/gen-sfrag-code frag locals) (let ((sem (sfrag-semantics frag)) ; If the frag has one owner, use it. Otherwise indicate the owner is ; unknown. In cases where the owner is needed by the semantics, the ; frag should have only one owner. In practice this means that frags ; with the ref,current-insn rtx cannot be used by multiple insns. (owner (if (= (length (sfrag-users frag)) 1) (car (sfrag-users frag)) #f))) ;; NOTE: (sfrag-users frag) is nil for the x-header and x-trailer frags. ;; They are just nops. (rtl-c++ VOID (and owner (obj-isa-list owner)) locals sem #:for-insn? #t #:rtl-cover-fns? #t #:owner owner)) ) ; Generate a switch case to perform FRAG. ; LOCALS is a list of sequence locals made global to all frags. ; Each element is (symbol "c-var-name"). (define (/gen-sfrag-case frag locals) (set! /with-profile? /with-profile-sw?) (let ((cti? (sfmt-cti? (sfrag-sfmt frag))) (parallel? (sfrag-parallel? frag))) (logit 2 "Processing " (if parallel? "parallel " "") "semantic switch case for " (obj:name frag) " ...\n") (string-list ; FRAG_ is prepended here and not elsewhere to avoid name collisions ; with symbols like AND, etc. "\ // ********** " (if (= (length (sfrag-users frag)) 1) "used only by:" "used by:") (string-drop1 (string-map (lambda (user) (string-append ", " (obj:str-name user))) (sfrag-users frag))) " CASE (FRAG_" (string-upcase (gen-sym frag)) "): {\n" (if (sfrag-header? frag) (string-append " abuf = vpc;\n" " vpc = vpc + 1;\n") "") (gen-define-field-macro (sfrag-sfmt frag)) ; Unconditionally written operands are not recorded here. (if (or (with-profile?) (with-parallel-write?)) " unsigned long long written = 0;\n" "") ; The address of this insn, needed by extraction and semantic code. ; Note that the address recorded in the cpu state struct is not used. " PCADDR pc = abuf->addr;\n" (if (and cti? (not parallel?) (sfrag-header? frag)) (string-append ; " npc = 0;\n" ??? needed? " br_status = BRANCH_UNTAKEN;\n") "") ; Emit setup-semantics code for headers of real insns. (if (and (sfrag-header? frag) (not (obj-has-attr? frag 'VIRTUAL)) (isa-setup-semantics (current-isa))) (string-append " " (rtl-c++ VOID (list (obj:name (current-isa))) nil (isa-setup-semantics (current-isa)) #:rtl-cover-fns? #t #:owner #f)) "") "\n" (/gen-sfrag-code frag locals) "\n" ; Only update what's been written if some are conditionally written. ; Otherwise we know they're all written so there's no point in ; keeping track. (if (or (with-profile?) (with-parallel-write?)) (if (/any-cond-written? (sfrag-sfmt frag)) " abuf->written = written;\n" "") "") (if (and cti? (not parallel?) (sfrag-trailer? frag)) (string-append " pbb_br_npc = npc;\n" " pbb_br_status = br_status;\n") "") (gen-undef-field-macro (sfrag-sfmt frag)) " }\n" (if (sfrag-trailer? frag) " NEXT_INSN (vpc, fragpc);\n" " NEXT_FRAG (fragpc);\n") "\n" )) ) ; Convert locals from form computed by sem-find-common-frags to that needed by ; /gen-sfrag-engine-code (and ultimately rtl-c++). (define (/frag-convert-c-locals locals) (map (lambda (local) (list (car local) (mode:lookup (cadr local)) (gen-c-symbol (car local)))) locals) ) ; Return definition of insn frag usage table. (define (/gen-sfrag-engine-frag-table insn-list frag-table frag-usage) (string-write "\ // Table of frags used by each insn. const @prefix@_insn_frag @prefix@_frag_usage[] = {\n" (lambda () (for-each (lambda (insn frag-nums) (string-write " { " "@PREFIX@_INSN_" (string-upcase (gen-sym insn)) (string-map (lambda (frag-num) (string-append ", @PREFIX@_FRAG_" (string-upcase (gen-sym (vector-ref frag-table frag-num))))) frag-nums) ", @PREFIX@_FRAG_LIST_END },\n")) insn-list frag-usage) "") "};\n\n") ) ; Return sfrag computed-goto engine. ; LOCALS is a list of sequence locals made global to all frags. ; Each element is (symbol "c-var-name"). (define (/gen-sfrag-engine-fn frag-table locals) (string-write "\ void @cpu@_cpu::@prefix@_pbb_run () { @cpu@_cpu* current_cpu = this; @prefix@_scache* vpc; @prefix@_scache* abuf; #ifdef __GNUC__ void** fragpc; #else ARM_FRAG_TYPE* fragpc; #endif #ifdef __GNUC__ { static const @prefix@_pbb_label labels[] = { { @PREFIX@_FRAG_LIST_END, 0 }, " (lambda () (string-write-map (lambda (frag) (string-append " { " "@PREFIX@_FRAG_" (string-upcase (gen-sym frag)) ", && case_FRAG_" (string-upcase (gen-sym frag)) " },\n")) ; FIXME: vector->list (vector->list frag-table))) "\ { @PREFIX@_FRAG_MAX, 0 } }; if (! @prefix@_idesc::idesc_table_initialized_p) { // Several tables are in play here: // idesc table: const table of misc things for each insn // frag usage table: const set of frags used by each insn // frag label table: same as frag usage table, but contains labels // selected insn frag table: table of pointers to either the frag usage // table (if !gnuc) or frag label table (if gnuc) for the currently // selected ISA. Insns not in the ISA are redirected to the `invalid' // insn handler. FIXME: This one isn't implemented yet. // Allocate frag label table and point idesc table entries at it. // FIXME: Temporary hack, to be redone. static void** frag_label_table; int max_insns = @PREFIX@_INSN_" (/last-insn) " + 1; int tabsize = max_insns * 4; frag_label_table = new void* [tabsize]; memset (frag_label_table, 0, sizeof (void*) * tabsize); int i; void** v; for (i = 0, v = frag_label_table; i < max_insns; ++i) { @prefix@_idesc::idesc_table[@prefix@_frag_usage[i].itype].cgoto.frags = v; for (int j = 0; @prefix@_frag_usage[i].ftype[j] != @PREFIX@_FRAG_LIST_END; ++j) *v++ = labels[@prefix@_frag_usage[i].ftype[j]].label; } // Initialize the compiler virtual insn. // FIXME: Also needed if !gnuc. current_cpu->@prefix@_engine.compile_begin_insn (current_cpu); @prefix@_idesc::idesc_table_initialized_p = true; } } #endif #ifdef __GNUC__ #define CASE(X) case_##X // Branch to next handler without going around main loop. #define NEXT_INSN(vpc, fragpc) fragpc = vpc->execute.cgoto.frags; goto * *fragpc #define NEXT_FRAG(fragpc) ++fragpc; goto * *fragpc // Break out of threaded interpreter and return to \"main loop\". #define BREAK(vpc) goto end_switch #else #define CASE(X) case @PREFIX@_##X #define NEXT_INSN(vpc, fragpc) fragpc = vpc->idesc->frags; goto restart #define NEXT_FRAG(fragpc) ++fragpc; goto restart #define BREAK(vpc) break #endif // Get next insn to execute. vpc = current_cpu->@prefix@_engine.get_next_vpc (current_cpu->h_pc_get ()); { // These two are used to pass data from cti insns to the cti-chain insn. PCADDR pbb_br_npc; branch_status pbb_br_status; // These two are used to build up values of the previous two. PCADDR npc; branch_status br_status; // Top level locals moved here so they're usable by multiple fragments. " (lambda () (string-write-map (lambda (local) (string-append " " (mode:c-type (cadr local)) " " (caddr local) ";\n")) locals)) "\ restart: #ifdef __GNUC__ fragpc = vpc->execute.cgoto.frags; goto * *fragpc; #else fragpc = vpc->idesc->frags; switch (*fragpc) #endif { " (lambda () ; Turn parallel execution support off. ; ??? Still needed? (set-with-parallel?! #f) (string-write-map (lambda (frag) (/gen-sfrag-case frag locals)) ; FIXME: vector->list (vector->list frag-table))) " #ifdef __GNUC__ end_switch: ; #else default: abort (); #endif } } // Save vpc for next time. current_cpu->@prefix@_engine.set_next_vpc (vpc); } \n") ) (define (/gen-sfrag-engine) (string-write (lambda () (/gen-sfrag-engine-frag-table (sim-sfrag-insn-list) (sim-sfrag-frag-table) (sim-sfrag-usage-table))) (lambda () (/gen-sfrag-engine-fn (sim-sfrag-frag-table) (/frag-convert-c-locals (sim-sfrag-locals-list)))) ) ) ; Generate sem-switch.cxx. (define (cgen-sem-switch.cxx) (logit 1 "Generating " (gen-cpu-name) "-sem-switch.cxx ...\n") (sim-analyze-insns!) (if (with-sem-frags?) (sim-sfrag-analyze-insns!)) ; Turn parallel execution support off. ; It is later turned on/off when generating the actual semantic code. (set-with-parallel?! #f) ; Tell the rtx->c translator we are the simulator. (rtl-c-config! #:rtl-cover-fns? #t) ; Indicate we're currently generating a pbb engine. (set-current-pbb-engine?! #t) (string-write (gen-c-copyright "Simulator instruction semantics for @prefix@." copyright-red-hat package-red-hat-simulators) "\ #include \"@cpu@.h\" using namespace @cpu@; // FIXME: namespace organization still wip #define GET_ATTR(name) GET_ATTR_##name () \n" (if (with-sem-frags?) /gen-sfrag-engine-decls "") (if (with-sem-frags?) /gen-sfrag-engine /gen-sem-switch-engine) ) )