URL
https://opencores.org/ocsvn/scarts/scarts/trunk
Subversion Repositories scarts
[/] [scarts/] [trunk/] [toolchain/] [scarts-binutils/] [binutils-2.19.1/] [cgen/] [sid-cpu.scm] - Rev 26
Go to most recent revision | Compare with Previous | Blame | View Log
; 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 ; Declare the attributes. "// Insn attribute indices.\n\n""cgen_insn""// Attributes.\n\n"; Generate class to hold an instruction's attributes. "// Insn attributes.\n\n"; FIXME: maybe make class, but that'll require a constructor. Later. "struct @arch@_insn_attr {\n"" unsigned int bools;\n"""" "" "";\n";"public:\n" " inline "" get_""_attr"" () { return ""(bools & ""cgen_insn"") != 0""; }\n""};\n\n"; Emit a macro that specifies the word-bitsize for each machine. "#define MACH_""_INSN_CHUNK_BITSIZE ""\n"; Generate <cpu>-desc.h. "Generating ""-desc.h ...\n""Misc. entries in the @arch@ description file.""\ #ifndef DESC_@ARCH@_H #define DESC_@ARCH@_H #include \"opcode/cgen-bitset.h\" namespace @arch@ { \n""// Enums.\n\n"" } // 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. "return ""regno"";""return this->hardware.""""[regno]"";"; not `mode', sets have mode VOID "newval""regno""newval""this->hardware.""""[regno]"" = newval;"" inline "" "" (""""UINT regno"") const"" { "" }""\n"" inline void "" (""""UINT regno, "" newval)"" { "" }""\n\n"; Return a boolean indicating if hardware element HW needs storage allocated ; for it in the SIM_CPU struct. ; Subroutine of -gen-hardware-types to generate the struct containing ; hardware elements of one isa. ; If struct is empty, leave it out to simplify generated code. """ // Hardware elements for "".\n"" // Hardware elements.\n"" struct {\n"" } ""_""""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. "// CPU state information.\n\n""0""_writes""_memory""hardware."" ost << "" << ' ';\n"" for (int i = 0; i < ""; i++)\n ost << ""[i] << ' ';\n""hardware."" ist >> "";\n"" for (int i = 0; i < ""; i++)\n ist >> ""[i];\n"" stream_stacks ( stacks."", ost);\n"" destream_stacks ( stacks."", ist);\n"" template <typename ST> \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 <typename ST> \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"" void stream_cgen_hardware (std::ostream &ost) const \n {\n"" }\n"" void destream_cgen_hardware (std::istream &ist) \n {\n"" }\n"" void stream_cgen_write_stacks (std::ostream &ost, ""const @prefix@::write_stacks &stacks) const \n {\n"" }\n"" void destream_cgen_write_stacks (std::istream &ist, ""@prefix@::write_stacks &stacks) \n {\n"" }\n"""; Generate <cpu>-cpu.h "Generating ""-cpu.h ...\n"; Turn parallel execution support on if cpu needs it. ; Initialize rtl->c generation. "CPU class elements for @cpu@.""\ // This file is included in the middle of the cpu class struct. public: \n"" // C++ register access function templates\n""#define current_cpu this\n\n""#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. "\ /* 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 ""\n\n""/* Maximum number of instructions that can be executed in parallel. */\n""#define @CPU@_MAX_PARALLEL_INSNS ""\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. "Generating model decls ...\n""typedef struct {\n"" int empty;\n"" "" "";\n""} ""BLANK""@CPU@""_MODEL_DATA;\n\n"" 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 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))))) ; 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)))) "_writes"" write_stack< write<""> >""\t""\t[pipe_sz];\n""write (PCADDR _pc, MODE _val"", USI _idx""=0"") : pc(_pc), val(_val)"", idx""(_idx"")"" {} \n"" USI idx"";\n""\n\n"" template <typename MODE>\n"" struct write\n"" {\n"" USI pc;\n"" MODE val;\n"" "" write() {}\n"" };\n"; for memory accesses "_memory""\n\n// write stacks used in parallel execution\n\n struct write_stacks\n {\n // types of stacks\n\n""\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 ();""\n\n }; // end struct @prefix@::write_stacks \n\n";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; end stack-based write schedule ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Generate the definition of the structure that holds register values, etc. ; for use during parallel execution. "Generating write stack structure ...\n"" static const int max_delay = "";\n"" static const int pipe_sz = ""; // max_delay + 1\n"" template <typename ELT> 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<sz ? t : sz-1) : 0];} }; // look ahead for latest write with index = idx, where time of write is // <= dist steps from base (present) in write_stack array st. // returning def if no scheduled write is found. template <typename STKS, typename VAL> inline VAL lookahead (int dist, int base, STKS &st, VAL def, int idx=0) { for (; dist > 0; --dist) { write_stack <VAL> &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; } "; Generate the TRACE_RECORD struct definition. "\ /* Collection of various things for the trace handler to use. */ typedef struct @prefix@_trace_record { PCADDR pc; /* FIXME:wip */ } @CPU@_TRACE_RECORD; \n"; Generate <cpu>-defs.h "Generating ""-defs.h ...\n"; Turn parallel execution support on if cpu needs it. ; Initialize rtl->c generation. "CPU family header for @cpu@ / @prefix@.""\ #ifndef DEFS_@PREFIX@_H #define DEFS_@PREFIX@_H ""\ #include <stack> #include \"cgen-types.h\" // forward declaration\n\n namespace @cpu@ { struct @cpu@_cpu; } namespace @prefix@ { using namespace cgen; ""\ } // end @prefix@ namespace ""\ #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 ; <sformat> SFMT. ; Generate <cpu>-write.cxx. " ""w.idx"", ""while (! ""_writes[tick].empty())\n""{\n"" write<""> &w = ""_writes[tick].top();\n"" current_cpu->""_set(""w.val);\n"" ""_writes[tick].pop();\n""}\n\n"" "", w.idx""""while (! ""_writes[tick].empty())\n""{\n"" write<""> &w = ""_writes[tick].top();\n"" current_cpu->SETMEM"" (w.pc"", w.val);\n"" ""_writes[tick].pop();\n""}\n\n""_memory"" clear_stacks (""_writes);\n"" template <typename ST> \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"" }""_memory""Generating writer function ...\n"" void @prefix@::write_stacks::writeback (int tick, @cpu@::@cpu@_cpu* current_cpu) { ""\n // register writeback loops\n""\n // memory writeback loops\n"" } ""Generating ""-write.cxx ...\n"; Turn parallel execution support off. ; Tell the rtx->c translator we are the simulator. "Simulator instruction operand writer for "".""\ #include \"@cpu@.h\" "; ****************** ; cgen-semantics.cxx ; Return C code to perform the semantics of INSN. ; Indicate generating code for INSN. ; Use the compiled form if available. ; The case when they're not available is for virtual insns. ; Return definition of C function to perform INSN. ; This version handles the with-scache case. "Processing semantics for "": \"""\" ...\n""// ********** "": ""\n\n""void\n""sem_status\n""@prefix@_sem_"" (@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"" sem_status status = SEM_STATUS_NORMAL;\n"" @prefix@_scache* abuf = sem;\n"; Unconditionally written operands are not recorded here. " 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 + "";\n""\n""\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. " abuf->written = written;\n"""""" current_cpu->done_cti_insn (npc, status);\n"" current_cpu->done_insn (npc, status);\n"""" return status;\n""}\n\n""Processing semantics ...\n""must specify `with-scache'"; Generate <cpu>-sem.cxx. ; Each instruction is implemented in its own function. "Generating ""-semantics.cxx ...\n"; Turn parallel execution support on if cpu needs it. ; Tell the rtx->c translator we are the simulator. ; Indicate we're currently not generating a pbb engine. "Simulator instruction semantics for @prefix@.""\ #if HAVE_CONFIG_H #include \"config.h\" #endif #include \"@cpu@.h\" using namespace @cpu@; // FIXME: namespace organization still wip\n""\ using namespace @prefix@; // FIXME: namespace organization still wip\n""\ #define GET_ATTR(name) GET_ATTR_##name () \n"; ******************* ; 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 <sformat> SFMT. ; ??? Not currently used. ; Utility of -gen-sem-case to return #t if any operand in <sformat> SFMT is ; conditionally written to. ; One case per insn version. ; Generate a switch case to perform INSN. "Processing ""parallel """"semantic switch case for \"""\" ...\n"; INSN_ is prepended here and not elsewhere to avoid name collisions ; with symbols like AND, etc. "\ // ********** "" CASE (INSN_""PAR_""""): { @prefix@_scache* abuf = vpc;\n"""; Unconditionally written operands are not recorded here. " 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"" PCADDR npc;\n"" branch_status br_status = BRANCH_UNTAKEN;\n"""" vpc = vpc + 1;\n"; Emit setup-semantics code for real insns. " """"\n""\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. " abuf->written = written;\n"""""" pbb_br_npc = npc;\n"" pbb_br_status = br_status;\n"""""" }\n"" NEXT (vpc);\n\n""Processing semantic switch ...\n"; Turn parallel execution support off. ; 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. "Processing parallel insn semantic switch ...\n"; Turn parallel execution support on. ; Return computed-goto engine. "\ 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"" { ""@PREFIX@_INSN_"", && case_INSN_"" },\n"" { ""@PREFIX@_INSN_PAR_"", && case_INSN_PAR_"" },\n"" { ""@PREFIX@_INSN_WRITE_"", && case_INSN_WRITE_"" },\n"""" { (@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_""; 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 { """" #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. "@prefix@_frag_type""semantic fragments in cpu family @prefix@""@PREFIX@_FRAG_"; Return header file decls for semantic frag threaded engine. "namespace @cpu@ {\n\n"; FIXME: vector->list "\ 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 <mode> "c-var-name"). ; Indicate generating code for FRAG. ; Use the compiled form if available. ; The case when they're not available is for virtual insns. ; 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. ; Generate a switch case to perform FRAG. ; LOCALS is a list of sequence locals made global to all frags. ; Each element is (symbol <mode> "c-var-name"). "Processing ""parallel """"semantic switch case for \"""\" ...\n"; FRAG_ is prepended here and not elsewhere to avoid name collisions ; with symbols like AND, etc. "\ // ********** ""used only by:""used by:"", "" CASE (FRAG_""): {\n"" abuf = vpc;\n"" vpc = vpc + 1;\n"""; Unconditionally written operands are not recorded here. " 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"; " npc = 0;\n" ??? needed? " br_status = BRANCH_UNTAKEN;\n"""; Emit setup-semantics code for headers of real insns. " """"\n""\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. " abuf->written = written;\n"""""" pbb_br_npc = npc;\n"" pbb_br_status = br_status;\n"""" }\n"" 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++). ; Return definition of insn frag usage table. "\ // Table of frags used by each insn. const @prefix@_insn_frag @prefix@_frag_usage[] = {\n"" { ""@PREFIX@_INSN_"", @PREFIX@_FRAG_"", @PREFIX@_FRAG_LIST_END },\n""""};\n\n"; Return sfrag computed-goto engine. ; LOCALS is a list of sequence locals made global to all frags. ; Each element is (symbol <mode> "c-var-name"). "\ 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 }, "" { ""@PREFIX@_FRAG_"", && case_FRAG_"" },\n"; FIXME: vector->list "\ { @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_"" + 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. "" "" "";\n""\ restart: #ifdef __GNUC__ fragpc = vpc->execute.cgoto.frags; goto * *fragpc; #else fragpc = vpc->idesc->frags; switch (*fragpc) #endif { "; Turn parallel execution support off. ; ??? Still needed? ; FIXME: vector->list " #ifdef __GNUC__ end_switch: ; #else default: abort (); #endif } } // Save vpc for next time. current_cpu->@prefix@_engine.set_next_vpc (vpc); } \n"; Generate sem-switch.cxx. "Generating ""-sem-switch.cxx ...\n"; Turn parallel execution support off. ; It is later turned on/off when generating the actual semantic code. ; Tell the rtx->c translator we are the simulator. ; Indicate we're currently generating a pbb engine. "Simulator instruction semantics for @prefix@.""\ #include \"@cpu@.h\" using namespace @cpu@; // FIXME: namespace organization still wip #define GET_ATTR(name) GET_ATTR_##name () \n"""
Go to most recent revision | Compare with Previous | Blame | View Log