; CPU family related simulator generator, excluding decoding and model support. ; Copyright (C) 2000, 2001, 2009 Red Hat, Inc. ; This file is part of CGEN. ; Notes: ; - Add support to generate copies of semantic code and perform constant ; folding based on selected mach. This would collapse out untaken branches ; of tests on (current-mach). ; Utilities of cgen-cpu.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). */ #define MAX_LIW_INSNS " (number->string (state-liw-insns)) "\n\ /* Maximum number of instructions that can be executed in parallel. */ #define MAX_PARALLEL_INSNS " (number->string (state-parallel-insns)) "\n\ /* The size of an \"int\" needed to hold an instruction word. This is usually 32 bits, but some architectures needs 64 bits. */ typedef " (if (adata-large-insn-word? CURRENT-ARCH) "CGEN_INSN_LGUINT" "CGEN_INSN_INT") " CGEN_INSN_WORD;\n\n" ; (gen-enum-decl '@cpu@_virtual ; "@cpu@ 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))) ) ) ; 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))) ) ; Subroutine of /gen-hardware-types to generate the struct containing ; hardware elements of one isa. (define (/gen-hardware-struct hw-list) (if (null? hw-list) ; If struct is empty, leave it out to simplify generated code. "" (string-list-map (lambda (hw) (string-list (gen-defn hw) (gen-obj-sanitize hw (string-list (send hw 'gen-get-macro) (send hw 'gen-set-macro))))) (find hw-need-storage? hw-list))) ) ; 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" "typedef struct {\n" " /* Hardware elements. */\n" " struct {\n" (/gen-hardware-struct (find (lambda (hw) (or (not (with-multiple-isa?)) (>= (count-common (current-keep-isa-name-list) (obj-attr-value hw 'ISA)) 1))) (current-hw-list)) ) " } hardware;\n" "#define CPU_CGEN_HW(cpu) (& (cpu)->cpu_data.hardware)\n" ;" /* CPU profiling state information. */\n" ;" struct {\n" ;(string-list-map (lambda (hw) (send hw 'gen-profile-decl)) ; (find hw-profilable? (current-hw-list))) ;" } profile;\n" ;"#define CPU_CGEN_PROFILE(cpu) (& (cpu)->cpu_data.profile)\n" "} @CPU@_CPU_DATA;\n\n" ; If there are any virtual regs, output get/set macros for them. (let ((virtual-regs (find (lambda (hw) (and (register? hw) (obj-has-attr? hw 'VIRTUAL))) (current-hw-list))) (orig-with-parallel? (with-parallel?)) (result "")) (set-with-parallel?! #f) (if (not (null? virtual-regs)) (set! result (string-list "/* Virtual regs. */\n\n" (string-list-map (lambda (hw) (logit 3 "Generating get/set for " (obj:name hw) " ...\n") (gen-obj-sanitize hw (string-list (send hw 'gen-get-macro) (send hw 'gen-set-macro)))) virtual-regs) "\n" ))) (set-with-parallel?! orig-with-parallel?) result) ) ) ; Return the declaration of register access functions. (define (/gen-cpu-reg-access-decls) (string-list "/* Cover fns for register access. */\n" (string-list-map (lambda (hw) (gen-reg-access-decl hw "@cpu@" (gen-type hw) (hw-scalar? hw))) (find register? (current-hw-list))) "\n" "/* These must be hand-written. */\n" "extern CPUREG_FETCH_FN @cpu@_fetch_register;\n" "extern CPUREG_STORE_FN @cpu@_store_register;\n" "\n") ) ; 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" ; ensure struct isn't empty so it compiles (string-map (lambda (var) (string-append " " (mode:c-type (mode:lookup (cadr var))) " " (gen-c-symbol (car var)) ";\n")) (model:state model))) "} MODEL_" (string-upcase (gen-sym model)) "_DATA;\n\n" )) (current-model-list)) ) ) ; Utility of /gen-extract-macros to generate a macro to define the local ; vars to contain extracted field values and the code to assign them ; for IFMT. (define (/gen-extract-ifmt-macro ifmt) (logit 2 "Processing format " (obj:name ifmt) " ...\n") (string-list (gen-define-ifmt-ifields ifmt "" #t #f) (gen-extract-ifmt-ifields ifmt "" #t #f) ; We don't need an extra blank line here as gen-extract-ifields adds one. ) ) ; Generate macros to extract instruction fields. (define (/gen-extract-macros) (logit 2 "Generating extraction macros ...\n") (string-list "\ /* Macros to simplify extraction, reading and semantic code. These define and assign the local vars that contain the insn's fields. */ \n" (string-list-map /gen-extract-ifmt-macro (current-ifmt-list)) ) ) ; Utility of /gen-parallel-exec-type to generate the definition of one ; structure in PAREXEC. ; SFMT is an object. (define (/gen-parallel-exec-elm sfmt) (string-append " struct { /* " (obj:comment sfmt) " */\n" (let ((sem-ops ((if (with-parallel-write?) sfmt-out-ops sfmt-in-ops) sfmt))) (if (null? sem-ops) " int empty;\n" ; ensure struct isn't empty so it compiles (string-map (lambda (op) (logit 2 "Processing operand " (obj:name op) " of format " (obj:name sfmt) " ...\n") (if (with-parallel-write?) (let ((index-type (and (op-save-index? op) (gen-index-type op sfmt)))) (string-append " " (gen-type op) " " (gen-sym op) ";\n" (if index-type (string-append " " index-type " " (gen-sym op) "_idx;\n") ""))) (string-append " " (gen-type op) " " (gen-sym op) ";\n"))) sem-ops))) " } " (gen-sym sfmt) ";\n" ) ) ; Generate the definition of the structure that holds register values, etc. ; for use during parallel execution. When instructions are executed parallelly ; either ; - their inputs are read before their outputs are written. Thus we have to ; fetch the input values of several instructions before executing any of them. ; - or their outputs are queued here first and then written out after all insns ; have executed. ; The fetched/queued values are stored in an array of PAREXEC structs, one ; element per instruction. (define (/gen-parallel-exec-type) (logit 2 "Generating PAREXEC type ...\n") (string-append (if (with-parallel-write?) "/* Queued output values of an instruction. */\n" "/* Fetched input values of an instruction. */\n") "\ struct parexec { union {\n" (string-map /gen-parallel-exec-elm (current-sfmt-list)) "\ } operands; /* For conditionally written operands, bitmask of which ones were. */ int written; };\n\n" ) ) ; Generate the TRACE_RECORD struct definition. ; This struct will hold all necessary data for doing tracing and profiling ; (e.g. register numbers). The goal is to remove all tracing code from the ; semantic code. Then the fast/full distinction needn't use conditionals to ; discard/include the tracing/profiling code. (define (/gen-trace-record-type) (string-list "\ /* Collection of various things for the trace handler to use. */ typedef struct trace_record { IADDR pc; /* FIXME:wip */ } TRACE_RECORD; \n" ) ) ; Utilities of cgen-cpu.c ; Get/set fns for every register. (define (/gen-cpu-reg-access-defns) (string-list-map (lambda (hw) (let ((scalar? (hw-scalar? hw)) (name (obj:name hw)) (getter (hw-getter hw)) (setter (hw-setter hw))) (gen-reg-access-defn hw "@cpu@" (gen-type hw) scalar? (if getter (string-append " return GET_" (string-upcase (gen-c-symbol name)) " (" (if scalar? "" "regno") ");\n") (string-append " return CPU (" (gen-c-symbol name) (if scalar? "" "[regno]") ");\n")) (if setter (string-append " SET_" (string-upcase (gen-c-symbol name)) " (" (if scalar? "" "regno, ") "newval);\n") (string-append " CPU (" (gen-c-symbol name) (if scalar? "" "[regno]") ") = newval;\n"))))) (find (lambda (hw) (register? hw)) (current-hw-list))) ) ; Generate a function to record trace results in a trace record. (define (/gen-cpu-record-results) (string-list "\ /* Record trace results for INSN. */ void @cpu@_record_trace_results (SIM_CPU *current_cpu, CGEN_INSN *insn, int *indices, TRACE_RECORD *tr) {\n" "}\n" ) ) ; Utilities of cgen-read.c. ; Parallel-read support is not currently used by any port and this code ; has been left to bitrot. Don't delete it just yet. ; Return C code to fetch and save all input operands to instructions with ; SFMT. (define (/gen-read-args sfmt) (string-map (lambda (op) (op:read op sfmt)) (sfmt-in-ops sfmt)) ) ; Utility of /gen-read-switch to generate a switch case for SFMT. (define (/gen-read-case sfmt) (logit 2 "Processing read switch case for \"" (obj:name sfmt) "\" ...\n") (string-list " CASE (read, READ_" (string-upcase (gen-sym sfmt)) ") : " "/* " (obj:comment sfmt) " */\n" " {\n" (gen-define-field-macro (if (with-scache?) sfmt #f)) (gen-define-parallel-operand-macro sfmt) (gen-define-ifields (sfmt-iflds sfmt) (sfmt-length sfmt) " " #f) (gen-extract-ifields (sfmt-iflds sfmt) (sfmt-length sfmt) " " #f) (/gen-read-args sfmt) (gen-undef-parallel-operand-macro sfmt) (gen-undef-field-macro sfmt) " }\n" " BREAK (read);\n\n" ) ) ; Generate the guts of a C switch statement to read insn operands. ; The switch is based on instruction formats. (define (/gen-read-switch) (logit 2 "Processing readers ...\n") (string-write-map /gen-read-case (current-sfmt-list)) ) ; Utilities of cgen-write.c. ; 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. ; ; There are separate implementations for semantics as functions and semantics ; as one big switch. For the function case we create a function that is a ; switch on each semantic format and loops writing each insn's results back. ; For the switch case we add cases to the switch to handle the write back, ; and it is up to the pbb compiler to include them in the generated "code". ; Return C code to fetch and save all output operands to instructions with ; SFMT. (define (/gen-write-args sfmt) (string-map (lambda (op) (op:write op sfmt)) (sfmt-out-ops sfmt)) ) ; Utility of gen-write-switch to generate a switch case for SFMT. ; If INSN is non-#f, it is the object of the insn in which case ; the case is named after the insn not the format. This is done because ; current sem-switch support emits one handler per insn instead of per sfmt. (define (/gen-write-case sfmt insn) (logit 2 "Processing write switch case for \"" (obj:name sfmt) "\" ...\n") (string-list (if insn (string-list /indent "CASE (sem, INSN_WRITE_" (string-upcase (gen-sym insn)) ") : ") (string-list /indent "case @CPU@_" (string-upcase (gen-sym sfmt)) " : ")) "/* " (if insn (string-list (insn-syntax insn)) (obj:comment sfmt)) " */\n" /indent " {\n" (if insn (string-list /indent " SEM_ARG sem_arg = SEM_SEM_ARG (vpc, sc);\n" /indent " const ARGBUF *abuf = SEM_ARGBUF (sem_arg)->fields.write.abuf;\n") "") (gen-define-field-macro (if (with-scache?) sfmt #f)) (gen-define-parallel-operand-macro sfmt) /indent " int UNUSED written = abuf->written;\n" ;(gen-define-ifields (sfmt-iflds sfmt) (sfmt-length sfmt) " " #f) - used by cgen-read.c ;(gen-extract-ifields (sfmt-iflds sfmt) (sfmt-length sfmt) " " #f) - used by cgen-read.c (if insn (string-list /indent " IADDR UNUSED pc = abuf->addr;\n") "") (if (and insn (insn-cti? insn)) (string-list /indent " SEM_BRANCH_INIT\n") ; no trailing `;' on purpose "") (if insn (string-list /indent " vpc = SEM_NEXT_VPC (sem_arg, pc, 0);\n") "") "\n" (/indent-add 4) (/gen-write-args sfmt) (/indent-add -4) "\n" (if (and insn (insn-cti? insn)) (string-list /indent " SEM_BRANCH_FINI (vpc);\n") "") (gen-undef-parallel-operand-macro sfmt) (gen-undef-field-macro sfmt) /indent " }\n" (if insn (string-list /indent " NEXT (vpc);\n") (string-list /indent " break;\n")) "\n" ) ) ; Generate the guts of a C switch statement to write insn operands. ; The switch is based on instruction formats. ; ??? This will generate cases for formats that don't need it. ; E.g. on the m32r all 32 bit insns can't be executed in parallel. ; It's easier to generate the code anyway so we do. (define (/gen-write-switch) (logit 2 "Processing writers ...\n") (string-write-map (lambda (sfmt) (/gen-write-case sfmt #f)) (current-sfmt-list)) ) ; Utilities of cgen-semantics.c. ; Return name of semantic fn for INSN. (define (/gen-sem-fn-name insn) ;(string-append "sem_" (gen-sym insn)) (gen-sym insn) ) ; Return semantic fn table entry for INSN. (define (/gen-sem-fn-table-entry insn) (string-list " { " "@PREFIX@_INSN_" (string-upcase (gen-sym insn)) ", " "SEM_FN_NAME (@prefix@," (/gen-sem-fn-name insn) ")" " },\n" ) ) ; Return C code to define a table of all semantic fns and a function to ; add the info to the insn descriptor table. (define (/gen-semantic-fn-table) (string-write "\ /* Table of all semantic fns. */ static const struct sem_fn_desc sem_fns[] = {\n" (lambda () (string-write-map /gen-sem-fn-table-entry (non-alias-insns (current-insn-list)))) "\ { 0, 0 } }; /* Add the semantic fns to IDESC_TABLE. */ void SEM_FN_NAME (@prefix@,init_idesc_table) (SIM_CPU *current_cpu) { IDESC *idesc_table = CPU_IDESC (current_cpu); const struct sem_fn_desc *sf; int mach_num = MACH_NUM (CPU_MACH (current_cpu)); for (sf = &sem_fns[0]; sf->fn != 0; ++sf) { const CGEN_INSN *insn = idesc_table[sf->index].idata; int valid_p = (CGEN_INSN_VIRTUAL_P (insn) || CGEN_INSN_MACH_HAS_P (insn, mach_num)); #if FAST_P if (valid_p) idesc_table[sf->index].sem_fast = sf->fn; else idesc_table[sf->index].sem_fast = SEM_FN_NAME (@prefix@,x_invalid); #else if (valid_p) idesc_table[sf->index].sem_full = sf->fn; else idesc_table[sf->index].sem_full = SEM_FN_NAME (@prefix@,x_invalid); #endif } } \n" ) ) ; Return C code to perform the semantics of INSN. (define (gen-semantic-code insn) (string-append (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") "") ; Indicate generating code for INSN. ; Use the compiled form if available. ; The case when they're not available is for virtual insns. xxx Still true? (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 ((profile? (and (with-profile?) (not (obj-has-attr? insn 'VIRTUAL)))) (parallel? (with-parallel?)) (cti? (insn-cti? insn)) (insn-len (insn-length-bytes insn))) (string-list "/* " (obj:str-name insn) ": " (insn-syntax insn) " */\n\n" "static SEM_PC\n" "SEM_FN_NAME (@prefix@," (gen-sym insn) ")" (if (and parallel? (not (with-generic-write?))) " (SIM_CPU *current_cpu, SEM_ARG sem_arg, PAREXEC *par_exec)\n" " (SIM_CPU *current_cpu, SEM_ARG sem_arg)\n") "{\n" (gen-define-field-macro (insn-sfmt insn)) (if (and parallel? (not (with-generic-write?))) (gen-define-parallel-operand-macro (insn-sfmt insn)) "") " ARGBUF *abuf = SEM_ARGBUF (sem_arg);\n" ; Unconditionally written operands are not recorded here. " int UNUSED 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. " IADDR UNUSED pc = abuf->addr;\n" (if (and cti? (not parallel?)) " SEM_BRANCH_INIT\n" ; no trailing `;' on purpose "") (string-list " SEM_PC vpc = SEM_NEXT_VPC (sem_arg, 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 (/any-cond-written? (insn-sfmt insn)) " abuf->written = written;\n" "") (if (and cti? (not parallel?)) " SEM_BRANCH_FINI (vpc);\n" "") " return vpc;\n" (if (and parallel? (not (with-generic-write?))) (gen-undef-parallel-operand-macro (insn-sfmt insn)) "") (gen-undef-field-macro (insn-sfmt insn)) "}\n\n" )) ) ; Return definition of C function to perform INSN. ; This version handles the without-scache case. ; ??? TODO: multiword insns. (define (/gen-no-scache-semantic-fn insn) (logit 2 "Processing semantics for " (obj:name insn) ": \"" (insn-syntax insn) "\" ...\n") (set! /with-profile? /with-profile-fn?) (let ((profile? (and (with-profile?) (not (obj-has-attr? insn 'VIRTUAL)))) (parallel? (with-parallel?)) (cti? (insn-cti? insn)) (insn-len (insn-length-bytes insn))) (string-list "/* " (obj:str-name insn) ": " (insn-syntax insn) " */\n\n" "static SEM_STATUS\n" "SEM_FN_NAME (@prefix@," (gen-sym insn) ")" (if (and parallel? (not (with-generic-write?))) " (SIM_CPU *current_cpu, SEM_ARG sem_arg, PAREXEC *par_exec, CGEN_INSN_WORD insn)\n" " (SIM_CPU *current_cpu, SEM_ARG sem_arg, CGEN_INSN_WORD insn)\n") "{\n" (if (and parallel? (not (with-generic-write?))) (gen-define-parallel-operand-macro (insn-sfmt insn)) "") " SEM_STATUS status = 0;\n" ; ??? wip " ARGBUF *abuf = SEM_ARGBUF (sem_arg);\n" (gen-define-field-macro (if (with-scache?) (insn-sfmt insn) #f)) ; Unconditionally written operands are not recorded here. " int UNUSED written = 0;\n" " IADDR UNUSED pc = GET_H_PC ();\n" (if (and cti? (not parallel?)) " SEM_BRANCH_INIT\n" ; no trailing `;' on purpose "") (string-list " SEM_PC vpc = SEM_NEXT_VPC (sem_arg, pc, " (number->string insn-len) ");\n") (string-list (gen-define-ifmt-ifields (insn-ifmt insn) " " #f #t) (gen-sfmt-op-argbuf-defns (insn-sfmt insn)) (gen-extract-ifmt-ifields (insn-ifmt insn) " " #f #t) (gen-sfmt-op-argbuf-assigns (insn-sfmt 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 (/any-cond-written? (insn-sfmt insn)) " abuf->written = written;\n" "") ; SEM_{,N}BRANCH_FINI are user-supplied macros. (if (not parallel?) (string-list (if cti? " SEM_BRANCH_FINI (vpc, " " SEM_NBRANCH_FINI (vpc, ") (gen-bool-attrs (obj-atlist insn) gen-attr-mask) ");\n") "") (gen-undef-field-macro (insn-sfmt insn)) " return status;\n" (if (and parallel? (not (with-generic-write?))) (gen-undef-parallel-operand-macro (insn-sfmt insn)) "") "}\n\n" )) ) (define (/gen-all-semantic-fns) (logit 2 "Processing semantics ...\n") (let ((insns (non-alias-insns (current-insn-list)))) (if (with-scache?) (string-write-map /gen-scache-semantic-fn insns) (string-write-map /gen-no-scache-semantic-fn insns))) ) ; 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))) ) ; 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. " CASE (sem, " "INSN_" (if parallel? "PAR_" "") (string-upcase (gen-sym insn)) ") : " "/* " (insn-syntax insn) " */\n" "{\n" " SEM_ARG sem_arg = SEM_SEM_ARG (vpc, sc);\n" " ARGBUF *abuf = SEM_ARGBUF (sem_arg);\n" (gen-define-field-macro (if (with-scache?) (insn-sfmt insn) #f)) (if (and parallel? (not (with-generic-write?))) (gen-define-parallel-operand-macro (insn-sfmt insn)) "") ; Unconditionally written operands are not recorded here. " int UNUSED 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. " IADDR UNUSED pc = abuf->addr;\n" (if (and cti? (not parallel?)) " SEM_BRANCH_INIT\n" ; no trailing `;' on purpose "") (if (with-scache?) "" (string-list (gen-define-ifmt-ifields (insn-ifmt insn) " " #f #t) (gen-extract-ifmt-ifields (insn-ifmt insn) " " #f #t) "\n")) (string-list " vpc = SEM_NEXT_VPC (sem_arg, 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 (/any-cond-written? (insn-sfmt insn)) " abuf->written = written;\n" "") (if (and cti? (not parallel?)) " SEM_BRANCH_FINI (vpc);\n" "") (if (and parallel? (not (with-generic-write?))) (gen-undef-parallel-operand-macro (insn-sfmt insn)) "") (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. (let ((orig-with-parallel? (with-parallel?))) (set-with-parallel?! #f) (let ((result (string-write-map (lambda (insn) (/gen-sem-case insn #f)) (non-alias-insns (current-insn-list))))) (set-with-parallel?! orig-with-parallel?) result)) ) ; 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. (let ((orig-with-parallel? (with-parallel?))) (set-with-parallel?! #t) (let ((result (string-write-map (lambda (insn) (string-list (/gen-sem-case insn #t) (/gen-write-case (insn-sfmt insn) insn))) (parallel-insns (current-insn-list))))) (set-with-parallel?! orig-with-parallel?) result)) ) ; Top level file generators. ; Generate cpu-.h (define (cgen-cpu.h) (logit 1 "Generating " (gen-cpu-name) "'s cpu.h ...\n") (sim-analyze-insns!) ; Turn parallel execution support on if cpu needs it. (set-with-parallel?! (state-parallel-exec?)) ; Tell the rtl->c translator we're not the simulator. ; ??? Minimizes changes in generated code until this is changed. ; RTL->C happens for field decoding. (rtl-c-config! #:rtl-cover-fns? #f) (string-write (gen-c-copyright "CPU family header for @cpu@." CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #ifndef CPU_@CPU@_H #define CPU_@CPU@_H " /gen-cpu-defines ;; After CGEN_INSN_WORD is defined we can include cgen-engine.h. ;; We need to include it here (or thereabouts) because cgen-engine.h ;; needs CGEN_INSN_WORD and parts of the remainder of this file need ;; cgen-engine.h. "#include \"cgen-engine.h\"\n\n" /gen-hardware-types /gen-cpu-reg-access-decls /gen-model-decls (if (not (with-multiple-isa?)) (string-list (lambda () (gen-argbuf-type #t)) (lambda () (gen-scache-type #t)) /gen-extract-macros) "") (if (and (with-parallel?) (not (with-generic-write?))) /gen-parallel-exec-type "") /gen-trace-record-type "#endif /* CPU_@CPU@_H */\n" ) ) ; Generate defs-.h. (define (cgen-defs.h) (logit 1 "Generating " (obj:name (current-isa)) "'s defs.h ...\n") (sim-analyze-insns!) ; Tell the rtl->c translator we're not the simulator. ; ??? Minimizes changes in generated code until this is changed. ; RTL->C happens for field decoding. (rtl-c-config! #:rtl-cover-fns? #f) (string-write (gen-c-copyright (string-append "ISA definitions header for " (obj:str-name (current-isa)) ".") CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #ifndef DEFS_@PREFIX@_H #define DEFS_@PREFIX@_H " (lambda () (gen-argbuf-type #t)) (lambda () (gen-scache-type #t)) /gen-extract-macros "#endif /* DEFS_@PREFIX@_H */\n" ) ) ; Generate cpu-.c (define (cgen-cpu.c) (logit 1 "Generating " (gen-cpu-name) "'s cpu.c ...\n") (sim-analyze-insns!) ; Turn parallel execution support on if cpu needs it. (set-with-parallel?! (state-parallel-exec?)) ; Initialize rtl generation. (rtl-c-config! #:rtl-cover-fns? #t) (string-write (gen-c-copyright "Misc. support for CPU family @cpu@." CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #define WANT_CPU @cpu@ #define WANT_CPU_@CPU@ #include \"sim-main.h\" #include \"cgen-ops.h\" " /gen-cpu-reg-access-defns /gen-cpu-record-results ) ) ; Generate read.c (define (cgen-read.c) (logit 1 "Generating " (gen-cpu-name) "'s read.c ...\n") (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 reader for " (symbol->string (current-arch-name)) ".") CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #ifdef DEFINE_LABELS /* The labels have the case they have because the enum of insn types is all uppercase and in the non-stdc case the fmt symbol is built into the enum name. */ static struct { int index; void *label; } labels[] = {\n" (lambda () (string-write-map (lambda (insn) (string-append " { " "@PREFIX@_INSN_" (string-upcase (gen-sym insn)) ", && case_read_READ_" (string-upcase (gen-sym (insn-sfmt insn))) " },\n")) (non-alias-insns (current-insn-list)))) " { 0, 0 } }; int i; for (i = 0; labels[i].label != 0; ++i) CPU_IDESC (current_cpu) [labels[i].index].read = labels[i].label; #undef DEFINE_LABELS #endif /* DEFINE_LABELS */ #ifdef DEFINE_SWITCH {\n" (if (with-scache?) "\ SEM_ARG sem_arg = sc; ARGBUF *abuf = SEM_ARGBUF (sem_arg); SWITCH (read, sem_arg->read)\n" "\ SWITCH (read, decode->read)\n") "\ { " /gen-read-switch "\ } ENDSWITCH (read) /* End of read switch. */ } #undef DEFINE_SWITCH #endif /* DEFINE_SWITCH */ " ) ) ; Generate write.c (define (cgen-write.c) (logit 1 "Generating " (gen-cpu-name) "'s write.c ...\n") (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)) ".") CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ /* Write cached results of 1 or more insns executed in parallel. */ void @cpu@_parallel_write (SIM_CPU *cpu, SCACHE *sbufs, PAREXEC *pbufs, int ninsns) {\n" (if (with-scache?) "\ SEM_ARG sem_arg = sc; ARGBUF *abuf = SEM_ARGBUF (sem_arg);\n" "") "\ do { ARGBUF *abuf = SEM_ARGBUF (sbufs); switch (abuf->idesc->write) { \n" ;(/indent-add 8) /gen-write-switch ;(/indent-add -8) "\ } } while (--ninsns > 0); } " ) ) ; Generate semantics.c ; Each instruction is implemented in its own function. (define (cgen-semantics.c) (logit 1 "Generating " (gen-cpu-name) "'s semantics.c ...\n") (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) (string-write (gen-c-copyright "Simulator instruction semantics for @cpu@." CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #define WANT_CPU @cpu@ #define WANT_CPU_@CPU@ #include \"sim-main.h\" #include \"cgen-mem.h\" #include \"cgen-ops.h\" #undef GET_ATTR " (gen-define-with-symcat "GET_ATTR(cpu, num, attr) \ CGEN_ATTR_VALUE (NULL, abuf->idesc->attrs, CGEN_INSN_" "attr)") " /* This is used so that we can compile two copies of the semantic code, one with full feature support and one without that runs fast(er). FAST_P, when desired, is defined on the command line, -DFAST_P=1. */ #if FAST_P #define SEM_FN_NAME(cpu,fn) XCONCAT3 (cpu,_semf_,fn) #undef TRACE_RESULT #define TRACE_RESULT(cpu, abuf, name, type, val) #else #define SEM_FN_NAME(cpu,fn) XCONCAT3 (cpu,_sem_,fn) #endif \n" /gen-all-semantic-fns ; Put the table at the end so we don't have to declare all the sem fns. /gen-semantic-fn-table ) ) ; Generate sem-switch.c. ; Each instruction is a case in a switch(). ; This file consists of just the switch(). It is included by mainloop.c. (define (cgen-sem-switch.c) (logit 1 "Generating " (gen-cpu-name) "'s sem-switch.c ...\n") (sim-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) (string-write (gen-c-copyright "Simulator instruction semantics for @cpu@." CURRENT-COPYRIGHT CURRENT-PACKAGE) "\ #ifdef DEFINE_LABELS /* The labels have the case they have because the enum of insn types is all uppercase and in the non-stdc case the insn symbol is built into the enum name. */ static struct { int index; void *label; } labels[] = {\n" (lambda () (string-write-map (lambda (insn) (string-append " { " "@PREFIX@_INSN_" (string-upcase (gen-sym insn)) ", && case_sem_INSN_" (string-upcase (gen-sym insn)) " },\n")) (non-alias-insns (current-insn-list)))) (if (state-parallel-exec?) (lambda () (string-write-map (lambda (insn) (string-append " { " "@CPU@_INSN_PAR_" (string-upcase (gen-sym insn)) ", && case_sem_INSN_PAR_" (string-upcase (gen-sym insn)) " },\n" " { " "@CPU@_INSN_WRITE_" (string-upcase (gen-sym insn)) ", && case_sem_INSN_WRITE_" (string-upcase (gen-sym insn)) " },\n")) (parallel-insns (current-insn-list)))) "") " { 0, 0 } }; int i; for (i = 0; labels[i].label != 0; ++i) { #if FAST_P CPU_IDESC (current_cpu) [labels[i].index].sem_fast_lab = labels[i].label; #else CPU_IDESC (current_cpu) [labels[i].index].sem_full_lab = labels[i].label; #endif } #undef DEFINE_LABELS #endif /* DEFINE_LABELS */ #ifdef DEFINE_SWITCH /* If hyper-fast [well not unnecessarily slow] execution is selected, turn off frills like tracing and profiling. */ /* FIXME: A better way would be to have TRACE_RESULT check for something that can cause it to be optimized out. Another way would be to emit special handlers into the instruction \"stream\". */ #if FAST_P #undef TRACE_RESULT #define TRACE_RESULT(cpu, abuf, name, type, val) #endif #undef GET_ATTR " (gen-define-with-symcat "GET_ATTR(cpu, num, attr) \ CGEN_ATTR_VALUE (NULL, abuf->idesc->attrs, CGEN_INSN_" "attr)") " { #if WITH_SCACHE_PBB /* Branch to next handler without going around main loop. */ #define NEXT(vpc) goto * SEM_ARGBUF (vpc) -> semantic.sem_case SWITCH (sem, SEM_ARGBUF (vpc) -> semantic.sem_case) #else /* ! WITH_SCACHE_PBB */ #define NEXT(vpc) BREAK (sem) #ifdef __GNUC__ #if FAST_P SWITCH (sem, SEM_ARGBUF (sc) -> idesc->sem_fast_lab) #else SWITCH (sem, SEM_ARGBUF (sc) -> idesc->sem_full_lab) #endif #else SWITCH (sem, SEM_ARGBUF (sc) -> idesc->num) #endif #endif /* ! WITH_SCACHE_PBB */ { " /gen-sem-switch (if (state-parallel-exec?) /gen-parallel-sem-switch "") " } ENDSWITCH (sem) /* End of semantic switch. */ /* At this point `vpc' contains the next insn to execute. */ } #undef DEFINE_SWITCH #endif /* DEFINE_SWITCH */ " ) ) ; Generate mainloop.in. ; ??? Not currently used. (define (cgen-mainloop.in) (logit 1 "Generating mainloop.in ...\n") (string-write "cat </dev/null\n" (gen-c-copyright "Simulator main loop for @arch@." CURRENT-COPYRIGHT CURRENT-PACKAGE) "EOF\n" "\ # Syntax: # /bin/sh mainloop.in init|support|{full,fast}-{extract,exec}-{scache,nocache} # ??? There's lots of conditional compilation here. # After a few more ports are done, revisit. case \"x$1\" in xsupport) cat <&2 exit 1 ;; esac " ) )