compiler_peg_mecpu.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /* */
00003 /*  Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net>*/
00004 /*  Parser Generator / Transformation - Compile grammar to ME cpu instructions.*/
00005 
00006 /*  This package assumes to be used from within a PAGE plugin. It uses*/
00007 /*  the API commands listed below. These are identical across the major*/
00008 /*  types of PAGE plugins, allowing this package to be used in reader,*/
00009 /*  transform, and writer plugins. It cannot be used in a configuration*/
00010 /*  plugin, and this makes no sense either.*/
00011 /* */
00012 /*  To ensure that our assumption is ok we require the relevant pseudo*/
00013 /*  package setup by the PAGE plugin management code.*/
00014 /* */
00015 /*  -----------------+--*/
00016 /*  page_info        | Reporting to the user.*/
00017 /*  page_warning     |*/
00018 /*  page_error       |*/
00019 /*  -----------------+--*/
00020 /*  page_log_error   | Reporting of internals.*/
00021 /*  page_log_warning |*/
00022 /*  page_log_info    |*/
00023 /*  -----------------+--*/
00024 
00025 /*  ### ### ### ######### ######### #########*/
00026 /*  Dumping the input grammar. But not as Tcl or other code. In PEG*/
00027 /*  format again, pretty printing.*/
00028 
00029 /*  ### ### ### ######### ######### #########*/
00030 /*  Requisites*/
00031 
00032 /*  @mdgen NODEP: page::plugin*/
00033 
00034 package require page::plugin ; /*  S.a. pseudo-package.*/
00035 
00036 package require grammar::me::cpu::gasm
00037 package require textutil
00038 package require struct::graph
00039 
00040 package require page::analysis::peg::emodes
00041 package require page::util::quote
00042 package require page::util::peg
00043 
00044 namespace ::page::compiler::peg::mecpu {
00045     /*  Get the peg char de/encoder commands.*/
00046     /*  (unquote, quote'tcl)*/
00047 
00048     namespace import ::page::util::quote::*
00049     namespace import ::page::util::peg::*
00050 
00051 
00052     namespace gas {
00053     namespace import ::grammar::me::cpu::gas::begin
00054     namespace import ::grammar::me::cpu::gas::done
00055     namespace import ::grammar::me::cpu::gas::lift
00056     namespace import ::grammar::me::cpu::gas::state
00057     namespace import ::grammar::me::cpu::gas::state!
00058     }
00059     namespace import ::grammar::me::cpu::gas::*
00060     rename begin  {}
00061     rename done   {}
00062     rename lift   {}
00063     rename state  {}
00064     rename state! {}
00065 }
00066 
00067 /*  ### ### ### ######### ######### #########*/
00068 /*  Data structures for the generated code.*/
00069 
00070 /*  All data is held in node attributes of the tree. Per node:*/
00071 /** 
00072  *# asm - List of instructions implementing the node.
00073  */
00074 
00075 
00076 
00077 /*  ### ### ### ######### ######### #########*/
00078 /*  API*/
00079 
00080 ret  ::page::compiler::peg::mecpu (type t) {
00081     # Resolve the mode hints. Every gen(X) having a value of 'maybe'
00082     # (or missing) is for the purposes of this code a 'yes'.
00083 
00084     if {![page::analysis::peg::emodes::compute $t]} {
00085     page_error "  Unable to generate a ME parser without accept/generate properties"
00086     return
00087     }
00088 
00089     foreach n [$t nodes] {
00090     if {![$t keyexists $n gen] || ([$t get $n gen] eq "maybe")} {
00091         $t set $n gen 1
00092     }
00093     if {![$t keyexists $n acc]} {$t set $n acc 1}
00094     }
00095 
00096     # Synthesize a program, then the assembly code.
00097 
00098     mecpu::Synth $t
00099     return
00100 }
00101 
00102 /*  ### ### ### ######### ######### #########*/
00103 /*  Internal. Helpers*/
00104 
00105 ret  ::page::compiler::peg::mecpu::Synth (type t) {
00106     # Phase 2: Bottom-up, synthesized attributes
00107 
00108     # We use a global graph to capture instructions and their
00109     # relations. The graph is then converted into a linear list of
00110     # instructions, with proper labeling and jump instructions to
00111     # handle all non-linear control-flow.
00112 
00113     set g [struct::graph g]
00114     $t set root gas::called {}
00115 
00116     page_info "* Synthesize graph code"
00117 
00118     $t walk root -order post -type dfs n {
00119     SynthNode $n
00120     }
00121 
00122     status             $g  ;  gdump $g synth
00123     remove_unconnected $g  ;  gdump $g nounconnected
00124     remove_dead        $g  ;  gdump $g nodead
00125     denop              $g  ;  gdump $g nonops
00126     parcmerge          $g  ;  gdump $g parcmerge
00127     forwmerge          $g  ;  gdump $g fmerge
00128     backmerge          $g  ;  gdump $g bmerge
00129     status             $g  
00130     pathlengths        $g  ;  gdump $g pathlen
00131     jumps              $g  ;  gdump $g jumps
00132     status             $g
00133     symbols            $g $t
00134 
00135     set cc [2code $t $g]
00136     #write asm/mecode [join $cc \n]
00137 
00138     statistics $cc
00139 
00140     $t set root asm $cc
00141     $g destroy
00142     return
00143 }
00144 
00145 ret  ::page::compiler::peg::mecpu::SynthNode (type n) {
00146     upvar 1 t t g g
00147     if {$n eq "root"} {
00148     set code Root
00149     } elseif {[$t keyexists $n symbol]} {
00150     set code Nonterminal
00151     } elseif {[$t keyexists $n op]} {
00152     set code [$t get $n op]
00153     } else {
00154     return -code error "PANIC. Bad node $n, cannot classify"
00155     }
00156 
00157     page_log_info "  [np $n] := ([linsert [$t children $n] 0 $code])"
00158 
00159     SynthNode/$code $n
00160     return
00161 }
00162 
00163 ret  ::page::compiler::peg::mecpu::SynthNode/Root (type n) {
00164     upvar 1 t t g g
00165 
00166     # Root is the grammar itself.
00167 
00168     set gstart [$t get root start]
00169     set gname  [$t get root name]
00170 
00171     if {$gstart eq ""} {
00172     page_error "  No start expression."
00173     return
00174     }
00175 
00176     gas::begin $g $n halt "<Start Expression> '$gname'"
00177     $g node set [Who entry] instruction .C
00178     $g node set [Who entry] START .
00179 
00180     Inline $t $gstart sexpr
00181     /At sexpr/exit/ok   ; /Ok   ; Jmp exit/return
00182     /At sexpr/exit/fail ; /Fail ; Jmp exit/return
00183 
00184     gas::done --> $t
00185     return
00186 }
00187 
00188 ret  ::page::compiler::peg::mecpu::SynthNode/Nonterminal (type n) {
00189     upvar 1 t t g g
00190 
00191     # This is the root of a definition.
00192     #
00193     # The text is a procedure wrapping the match code of its
00194     # expression into the required the nonterminal handling (caching
00195     # and such), plus the support code for the expression matcher.
00196 
00197     set sym      [$t get $n symbol]
00198     set label    [$t get $n label]
00199     set gen      [$t get $n gen]
00200     set mode     [$t get $n mode]
00201 
00202     set pe       [lindex [$t children $n] 0]
00203     set egen     [$t get $pe gen]
00204 
00205     # -> inc_restore -found-> NOP  gen:  -> ok -> ias_push -> RETURN
00206     #               /!found             \                  /
00207     #              /                     \-fail --------->/
00208     #             /               !gen: -> RETURN
00209     #            /
00210     #            \-> icl_push (-> ias_mark) -> (*) -> SV -> inc_save (-> ias_mrewind) -X
00211     #
00212     # X -ok----> ias_push -> ier_nonterminal
00213     #  \                  /
00214     #   \-fail ----------/
00215 
00216     # Poking into the generated instructions, converting the initial
00217     # .NOP into a .C'omment.
00218 
00219     set first [gas::begin $g $n !okfail "Nonterminal '$sym'"]
00220     $g node set [Who entry] instruction .C
00221     $g node set [Who entry] START .
00222 
00223     Cmd inc_restore $label ; /Label restore ; /Ok
00224 
00225     if {$gen} {
00226     Bra ; /Label @
00227     /Fail ; Nop          ; Exit
00228     /At @
00229     /Ok   ; Cmd ias_push ; Exit
00230     } else {
00231     Nop ; Exit
00232     }
00233 
00234     /At restore ; /Fail
00235     Cmd icl_push ; # Balanced by inc_save (XX)
00236     Cmd icl_push ; # Balanced by pop after ier_terminal
00237 
00238     if {$egen} {
00239     # [*] Needed for removal of SV's from stack after handling by
00240     # this symbol, only if expression actually generates an SV.
00241 
00242     Cmd ias_mark
00243     }
00244 
00245     Inline $t $pe subexpr ; /Ok   ; Nop ; /Label unified
00246     /At subexpr/exit/fail ; /Fail ; Jmp unified
00247     /At unified
00248 
00249     switch -exact -- $mode {
00250     value   {Cmd isv_nonterminal_reduce $label}
00251     match   {Cmd isv_nonterminal_range  $label}
00252     leaf    {Cmd isv_nonterminal_leaf   $label}
00253     discard {Cmd isv_clear}
00254     default {return -code error "Bad nonterminal mode \"$mode\""}
00255     }
00256 
00257     Cmd inc_save $label ; # Implied icl_pop (XX)
00258 
00259     if {$egen} {
00260     # See [*], this is the removal spoken about before.
00261     Cmd ias_mrewind
00262     }
00263 
00264     /Label hold
00265 
00266     if {$gen} {
00267     /Ok
00268     Cmd ias_push
00269     Nop           ; /Label merge
00270     /At hold ; /Fail ; Jmp merge
00271     /At merge
00272     }
00273 
00274     Cmd ier_nonterminal "Expected $label"
00275     Cmd icl_pop
00276     Exit
00277 
00278     gas::done --> $t
00279     return
00280 }
00281 
00282 ret  ::page::compiler::peg::mecpu::SynthNode/? (type n) {
00283     upvar 1 t t g g
00284 
00285     # The expression e? is equivalent to e/epsilon.
00286     # And like this it is compiled.
00287 
00288     set pe       [lindex [$t children $n] 0]
00289 
00290     gas::begin $g $n okfail ?
00291 
00292     # -> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop -ok----------------> OK
00293     #                             \                                                    /
00294     #                              \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -ok-/
00295 
00296     Cmd icl_push
00297     Cmd ier_push
00298 
00299     Inline $t $pe subexpr
00300 
00301     /Ok
00302     Cmd ier_merge
00303     Cmd icl_pop
00304     /Ok ; Exit
00305 
00306     /At subexpr/exit/fail ; /Fail
00307     Cmd ier_merge
00308     Cmd icl_rewind
00309     Cmd iok_ok
00310     /Ok ; Exit
00311 
00312     gas::done --> $t
00313     return
00314 }
00315 
00316 ret  ::page::compiler::peg::mecpu::SynthNode/* (type n) {
00317     upvar 1 t t g g
00318 
00319     # Kleene star is like a repeated ?
00320 
00321     # Note: Compilation as while loop, as done now
00322     # means that the parser has no information about
00323     # the intermediate structure of the input in his
00324     # cache.
00325 
00326     # Future: Create a helper symbol X and compile
00327     # the expression e = e'* as:
00328     #     e = X; X <- (e' X)?
00329     # with match data for X put into the cache. This
00330     # is not exactly equivalent, the structure of the
00331     # AST is different (right-nested tree instead of
00332     # a list). This however can be handled with a
00333     # special nonterminal mode to expand the current
00334     # SV on the stack.
00335 
00336     # Note 2: This is a transformation which can be
00337     # done on the grammar itself, before the actual
00338     # backend is let loose. This "strength reduction"
00339     # allows us to keep this code here.
00340 
00341     set pe       [lindex [$t children $n] 0]
00342     set egen     [$t get $pe gen]
00343 
00344     # Build instruction graph.
00345 
00346     #  /<---------------------------------------------------------------\
00347     #  \_                                                                \_
00348     # ---> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop ->/
00349     #                               \
00350     #                                \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -> OK
00351 
00352     gas::begin $g $n okfail *
00353 
00354     Cmd icl_push ; /Label header
00355     Cmd ier_push
00356 
00357     Inline $t $pe loop
00358 
00359     /Ok
00360     Cmd ier_merge
00361     Cmd icl_pop
00362     Jmp header ; /CloseLoop
00363 
00364     /At loop/exit/fail ; /Fail
00365     Cmd ier_merge
00366     Cmd icl_rewind
00367     Cmd iok_ok
00368     /Ok ; Exit
00369 
00370     gas::done --> $t
00371     return
00372 }
00373 
00374 ret  ::page::compiler::peg::mecpu::SynthNode/+ (type n) {
00375     upvar 1 t t g g
00376 
00377     # Positive Kleene star x+ is equivalent to x x*
00378     # This is how it is compiled. See also the notes
00379     # at the * above, they apply in essence here as
00380     # well, except that the transformat scheme is
00381     # slighty different:
00382     #
00383     # e = e'*  ==> e = X; X <- e' X?
00384 
00385     set pe [lindex [$t children $n] 0]
00386 
00387     # Build instruction graph.
00388 
00389     # icl_push -> ier_push -> (*) -fail-> ier_merge/fl -> icl_rewind -> FAIL
00390     #                          \
00391     #                           \--ok---> ier_merge/ok -> icl_pop ->\_
00392     #                                                               /
00393     #    /<--------------------------------------------------------/
00394     #   /
00395     #  /<---------------------------------------------------------------\
00396     #  \_                                                                \_
00397     #   -> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop ->/
00398     #                               \
00399     #                                \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -> OK
00400 
00401     gas::begin $g $n okfail +
00402 
00403     Cmd icl_push
00404     Cmd ier_push
00405 
00406     Inline $t $pe first
00407     /At first/exit/fail ; /Fail
00408     Cmd ier_merge
00409     Cmd icl_rewind
00410     /Fail ; Exit
00411 
00412     /At first/exit/ok ; /Ok
00413     Cmd ier_merge
00414     Cmd icl_pop
00415 
00416     # Loop copied from Kleene *, it is *
00417 
00418     Cmd icl_push ; /Label header
00419     Cmd ier_push
00420 
00421     # For the loop we create the sub-expression instruction graph a
00422     # second time. This is done by walking the subtree a second time
00423     # and constructing a completely new node set. The result is
00424     # imported under a new name.
00425 
00426     set save [gas::state]
00427     $t walk $pe -order post -type dfs n {SynthNode $n}
00428     gas::state! $save
00429     Inline $t $pe loop
00430 
00431     /Ok
00432     Cmd ier_merge
00433     Cmd icl_pop
00434     Jmp header ; /CloseLoop
00435 
00436     /At loop/exit/fail ; /Fail
00437     Cmd ier_merge
00438     Cmd icl_rewind
00439     Cmd iok_ok
00440     /Ok ; Exit
00441 
00442     gas::done --> $t
00443     return
00444 }
00445 
00446 ret  ::page::compiler::peg::mecpu::SynthNode// (type n) {
00447     upvar 1 t t g g
00448 
00449     set args [$t children $n]
00450 
00451     if {![llength $args]} {
00452     error "PANIC. Empty choice."
00453 
00454     } elseif {[llength $args] == 1} {
00455     # A choice over one branch is no real choice. The code
00456     # generated for the child applies here as well.
00457 
00458     gas::lift $t $n <-- [lindex $args 0]
00459     return
00460     }
00461 
00462     # Choice over at least two branches.
00463     # Build instruction graph.
00464 
00465     # -> BRA
00466     #
00467     # BRA -> icl_push (-> ias_mark) -> ier_push -> (*) -ok -> ier_merge -> BRA'OK
00468     #                                              \-fail -> ier_merge (-> ias_mrewind) -> icl_rewind -> BRA'FAIL
00469     #
00470     # BRA'FAIL -> BRA
00471     # BRA'FAIL -> FAIL (last branch)
00472     #
00473     # BRA'OK -> icl_pop -> OK
00474 
00475     gas::begin $g $n okfail /
00476 
00477     /Clear
00478     Cmd icl_pop ; /Label BRA'OK ; /Ok ; Exit
00479     /At entry
00480 
00481     foreach pe $args {
00482     set egen [$t get $pe gen]
00483 
00484     # Note: We do not check for static match results. Doing so is
00485     # an optimization we can do earlier, directly on the tree.
00486 
00487     Cmd icl_push
00488     if {$egen} {Cmd ias_mark}
00489 
00490     Cmd ier_push
00491     Inline $t $pe subexpr
00492 
00493     /Ok
00494     Cmd ier_merge
00495     Jmp BRA'OK
00496 
00497     /At subexpr/exit/fail ; /Fail
00498     Cmd ier_merge
00499     if {$egen} {Cmd ias_mrewind}
00500     Cmd icl_rewind
00501 
00502     # Branch failed. Go to the next branch. Fail completely at
00503     # last branch.
00504     }
00505 
00506     /Fail ; Exit
00507 
00508     gas::done --> $t
00509     return
00510 }
00511 
00512 ret  ::page::compiler::peg::mecpu::SynthNode/x (type n) {
00513     upvar 1 t t g g
00514 
00515     set args [$t children $n]
00516 
00517     if {![llength $args]} {
00518     error "PANIC. Empty sequence."
00519 
00520     } elseif {[llength $args] == 1} {
00521     # A sequence of one element is no real sequence. The code
00522     # generated for the child applies here as well.
00523 
00524     gas::lift $t $n <-- [lindex $args 0]
00525     return
00526     }
00527 
00528     # Sequence of at least two elements.
00529     # Build instruction graph.
00530 
00531     # -> icl_push -> SEG
00532     #
00533     # SEG (-> ias_mark) -> ier_push -> (*) -ok -> ier_merge -> SEG'OK
00534     #                                  \-fail -> ier_merge -> SEG'FAIL
00535     #
00536     # SEG'OK -> SEG
00537     # SEG'OK -> icl_pop -> OK (last segment)
00538     #
00539     # SEG'FAIL (-> ias_mrewind) -> icl_rewind -> FAIL
00540 
00541     gas::begin $g $n okfail x
00542 
00543     /Clear
00544     Cmd icl_rewind ; /Label SEG'FAIL ; /Fail ; Exit
00545 
00546     /At entry
00547     Cmd icl_push
00548 
00549     set gen 0
00550     foreach pe $args {
00551     set egen [$t get $pe gen]
00552     if {$egen && !$gen} {
00553         set gen 1
00554 
00555         # From here on out is the sequence able to generate
00556         # semantic values which have to be canceled when
00557         # backtracking.
00558 
00559         Cmd ias_mark ; /Label @mark
00560 
00561         /Clear
00562         Cmd ias_mrewind ; Jmp SEG'FAIL ; /Label SEG'FAIL
00563 
00564         /At @mark
00565     }
00566 
00567     Cmd ier_push
00568     Inline $t $pe subexpr
00569 
00570     /At subexpr/exit/fail ; /Fail
00571     Cmd ier_merge
00572     Jmp SEG'FAIL
00573 
00574     /At subexpr/exit/ok ; /Ok
00575     Cmd ier_merge 
00576     }
00577 
00578     Cmd icl_pop
00579     /Ok ; Exit
00580 
00581     gas::done --> $t
00582     return
00583 }
00584 
00585 ret  ::page::compiler::peg::mecpu::SynthNode/& (type n) {
00586     upvar 1 t t g g
00587     SynthLookahead $n no
00588     return
00589 }
00590 
00591 ret  ::page::compiler::peg::mecpu::SynthNode/! (type n) {
00592     upvar 1 t t g g
00593     SynthLookahead $n yes
00594     return
00595 }
00596 
00597 ret  ::page::compiler::peg::mecpu::SynthNode/dot (type n) {
00598     upvar 1 t t g g
00599     SynthTerminal $n {} "any character"
00600     return
00601 }
00602 
00603 ret  ::page::compiler::peg::mecpu::SynthNode/epsilon (type n) {
00604     upvar 1 t t g g
00605 
00606     gas::begin $g $n okfail epsilon
00607 
00608     Cmd iok_ok ; /Ok ; Exit
00609 
00610     gas::done --> $t
00611     return
00612 }
00613 
00614 ret  ::page::compiler::peg::mecpu::SynthNode/alnum (type n) {
00615     upvar 1 t t g g
00616     SynthClass $n alnum
00617     return
00618 }
00619 
00620 ret  ::page::compiler::peg::mecpu::SynthNode/alpha (type n) {
00621     upvar 1 t t g g
00622     SynthClass $n alpha
00623     return
00624 }
00625 
00626 ret  ::page::compiler::peg::mecpu::SynthNode/digit (type n) {
00627     upvar 1 t t g g
00628     SynthClass $n digit
00629     return
00630 }
00631 
00632 ret  ::page::compiler::peg::mecpu::SynthNode/xdigit (type n) {
00633     upvar 1 t t g g
00634     SynthClass $n xdigit
00635     return
00636 }
00637 
00638 ret  ::page::compiler::peg::mecpu::SynthNode/punct (type n) {
00639     upvar 1 t t g g
00640     SynthClass $n punct
00641     return
00642 }
00643 
00644 ret  ::page::compiler::peg::mecpu::SynthNode/space (type n) {
00645     upvar 1 t t g g
00646     SynthClass $n space
00647     return
00648 }
00649 
00650 ret  ::page::compiler::peg::mecpu::SynthNode/.. (type n) {
00651     upvar 1 t t g g
00652     # Range is [x-y]
00653 
00654     set b [$t get $n begin]
00655     set e [$t get $n end]
00656 
00657     set tb [quote'tcl $b]
00658     set te [quote'tcl $e]
00659 
00660     set pb [quote'tclstr $b]
00661     set pe [quote'tclstr $e]
00662 
00663     SynthTerminal $n [list ict_match_tokrange $tb $te] "\\\[${pb}..${pe}\\\]"
00664     return
00665 }
00666 
00667 ret  ::page::compiler::peg::mecpu::SynthNode/t (type n) {
00668     upvar 1 t t g g
00669 
00670     # Terminal node. Primitive matching.
00671     # Code is parameterized by gen(X) of this node X.
00672 
00673     set ch  [$t get $n char]
00674     set tch [quote'tcl    $ch]
00675     set pch [quote'tclstr $ch]
00676 
00677     SynthTerminal $n [list ict_match_token $tch] $pch
00678     return
00679 }
00680 
00681 ret  ::page::compiler::peg::mecpu::SynthNode/n (type n) {
00682     upvar 1 t t g g
00683 
00684     # Nonterminal node. Primitive matching.
00685     # The code is parameterized by acc(X) of this node X, and gen(D)
00686     # of the invoked nonterminal D.
00687 
00688     set sym   [$t get $n sym]
00689     set def   [$t get $n def]
00690 
00691     gas::begin $g $n okfail call'$sym'
00692 
00693     if {$def eq ""} {
00694     # Invokation of an undefined nonterminal. This will always fail.
00695 
00696     Note "Match for undefined symbol '$sym'"
00697     Cmdd iok_fail ; /Fail ; Exit
00698     gas::done --> $t
00699 
00700     } else {
00701     # Combinations
00702     # Acc Gen Action
00703     # --- --- ------
00704     #   0   0 Plain match
00705     #   0   1 Match with canceling of the semantic value.
00706     #   1   0 Plain match
00707     #   1   1 Plain match
00708     # --- --- ------
00709 
00710     if {[$t get $n acc] || ![$t get $def gen]} {
00711         Cmd icf_ntcall sym_$sym ; /Label CALL
00712         /Ok   ; Exit
00713         /Fail ; Exit
00714 
00715     } else {
00716         Cmd ias_mark
00717         Cmd icf_ntcall sym_$sym ; /Label CALL
00718         Cmd ias_mrewind
00719         /Ok   ; Exit
00720         /Fail ; Exit
00721     }
00722 
00723     set caller [Who CALL]
00724     gas::done --> $t
00725 
00726     $t lappend $def gas::callers $caller
00727     $t lappend root gas::called  $def
00728     }
00729 
00730     return
00731 }
00732 
00733 ret  ::page::compiler::peg::mecpu::SynthLookahead (type n , type negated) {
00734     upvar 1 g g t t
00735 
00736     # Note: Per the rules about expression modes (! is a lookahead
00737     # ____| operator) this node has a mode of 'discard', and its child
00738     # ____| has so as well.
00739 
00740     # assert t get n  mode == discard
00741     # assert t get pe mode == discard
00742 
00743     set op       [$t get $n op]
00744     set pe       [lindex [$t children $n] 0]
00745     set eop      [$t get $pe op]
00746 
00747     # -> icl_push -> (*) -ok--> icl_rewind -> OK
00748     #                 \--fail-> icl_rewind -> FAIL
00749 
00750     # -> icl_push -> (*) -ok--> icl_rewind -> iok_negate -> FAIL
00751     #                 \--fail-> icl_rewind -> iok_negate -> OK
00752 
00753     gas::begin $g $n okfail [expr {$negated ? "!" : "&"}]
00754 
00755     Cmd icl_push
00756     Inline $t $pe subexpr
00757 
00758     /Ok
00759     Cmd icl_rewind
00760     if {$negated} { Cmd iok_negate ; /Fail } else /Ok ; Exit
00761 
00762     /At subexpr/exit/fail ; /Fail
00763     Cmd icl_rewind
00764     if {$negated} { Cmd iok_negate ; /Ok } else /Fail ; Exit
00765 
00766     gas::done --> $t
00767     return
00768 }
00769 
00770 ret  ::page::compiler::peg::mecpu::SynthClass (type n , type op) {
00771     upvar 1 t t g g
00772     SynthTerminal $n [list ict_match_tokclass $op] <$op>
00773     return
00774 }
00775 
00776 ret  ::page::compiler::peg::mecpu::SynthTerminal (type n , type cmd , type msg) {
00777     upvar 1 t t g g
00778 
00779     # 4 cases (+/- cmd, +/- sv).
00780     #
00781     # (A) +cmd+sv
00782     #     entry -> advance -ok-> match -ok-> sv -> OK
00783     #              \             \
00784     #               \             \-fail----------> FAIL
00785     #                \-fail----------------------/
00786     #
00787     # (B) -cmd+sv
00788     #     entry -> advance -ok-> sv -> OK
00789     #              \
00790     #               \-fail-----------> FAIL
00791     #
00792     # (C) +cmd-sv
00793     #     entry -> advance -ok-> match -ok-> OK
00794     #              \             \
00795     #               \             \-fail---> FAIL
00796     #                \-fail---------------/
00797     #
00798     # (D) -cmd-sv
00799     #     entry -> advance -ok-> OK
00800     #              \
00801     #               \-fail-----> FAIL
00802 
00803     gas::begin $g $n okfail M'[lindex $cmd 0]
00804 
00805     Cmd ict_advance "Expected $msg (got EOF)"
00806     /Fail ; Exit
00807     /Ok
00808 
00809     if {[llength $cmd]} {
00810     lappend cmd "Expected $msg"
00811     eval [linsert $cmd 0 Cmd]
00812     /Fail ; Exit
00813     /Ok
00814     }
00815 
00816     if {[$t get $n gen]} {
00817     Cmd isv_terminal
00818     /Ok
00819     }
00820 
00821     Exit
00822 
00823     gas::done --> $t
00824     return
00825 }
00826 
00827 /*  ### ### ### ######### ######### #########*/
00828 /*  Internal. Extending the graph of instructions (expression*/
00829 /*  framework, new instructions, (un)conditional sequencing).*/
00830 
00831 /*  ### ### ### ######### ######### #########*/
00832 /*  Internal. Working on the graph of instructions.*/
00833 
00834 ret  ::page::compiler::peg::mecpu::2code (type t , type g) {
00835     page_info "* Generating ME assembler code"
00836 
00837     set insn  {}
00838     set start [$t get root gas::entry]
00839     set cat 0
00840     set calls [list $start]
00841 
00842     while {$cat < [llength $calls]} {
00843     set  now [lindex $calls $cat]
00844     incr cat
00845 
00846     set at 0
00847     set pending [list $now]
00848 
00849     while {$at < [llength $pending]} {
00850         set  current [lindex $pending $at]
00851         incr at
00852 
00853         while {$current ne ""} {
00854         if {[$g node keyexists $current WRITTEN]} break
00855 
00856         insn $g $current insn
00857         $g node set $current WRITTEN .
00858 
00859         if {[$g node keyexists $current SAVE]} {
00860             lappend pending [$g node get $current SAVE]
00861         }
00862         if {[$g node keyexists $current CALL]} {
00863             lappend calls [$g node get $current CALL]
00864         }
00865 
00866         set  current [$g node get $current NEXT]
00867         if {$current eq ""} break
00868         if {[$g node keyexists $current WRITTEN]} {
00869             lappend insn [list {} icf_jalways \
00870                 [$g node get $current LABEL]]
00871             break
00872         }
00873 
00874         # Process the following instruction,
00875         # if there is any.
00876         }
00877     }
00878     }
00879 
00880     return $insn
00881 }
00882 
00883 ret  ::page::compiler::peg::mecpu::insn (type g , type current , type iv) {
00884     upvar 1 $iv insn
00885 
00886     set code [$g node get $current instruction]
00887     set args [$g node get $current arguments]
00888 
00889     set label {}
00890     if {[$g node keyexists $current LABEL]} {
00891     set label [$g node get $current LABEL]
00892     }
00893 
00894     lappend insn [linsert $args 0 $label $code]
00895     return
00896 }
00897 
00898 if 0 {
00899     if {[lindex $ins 0] eq "icf_ntcall"} {
00900      tmp =  {}
00901     foreach b $branches {
00902         if {[$g node keyexists $b START]} {
00903          sym =  [$g node get $b symbol]
00904         lappend ins     sym_$sym
00905         } else {
00906         lappend tmp $b
00907         }
00908     }
00909      branches =  $tmp
00910     }
00911 }
00912 
00913 /*  ### ### ### ######### ######### #########*/
00914 /*  Optimizations.*/
00915 /* */
00916 /*  I. Remove all nodes which are not connected to anything.*/
00917 /*     There should be none.*/
00918 
00919 ret  ::page::compiler::peg::mecpu::remove_unconnected (type g) {
00920     page_info "* Remove unconnected instructions"
00921 
00922     foreach n [$g nodes] {
00923     if {[$g node degree $n] == 0} {
00924         page_error "$n ([printinsn $g $n])"
00925         page_error "Found unconnected node. This should not have happened."
00926         page_error "Removing the bad node."
00927 
00928         $g node delete $n
00929     }
00930     }
00931 }
00932 
00933 ret  ::page::compiler::peg::mecpu::remove_dead (type g) {
00934     page_info "* Remove dead instructions"
00935 
00936     set count 0
00937     set runs 0
00938     set hasdead 1
00939     while {$hasdead} {
00940     set hasdead 0
00941     foreach n [$g nodes] {
00942         if {[$g node keyexists $n START]} continue
00943         if {[$g node degree -in $n] > 0}  continue
00944 
00945         page_log_info "    [np $n] removed, dead ([printinsn $g $n])"
00946 
00947         $g node delete $n
00948 
00949         set hasdead 1
00950         incr count
00951     }
00952     incr runs
00953     }
00954 
00955     page_info "  Removed [plural $count instruction] in [plural $runs run]"
00956     return
00957 }
00958 
00959 /*  ### ### ### ######### ######### #########*/
00960 /*  Optimizations.*/
00961 /* */
00962 /*  II. We have lots of .NOP instructions in the control flow, as part*/
00963 /*      of the framework. They made the handling of expressions easier,*/
00964 /*      providing clear and fixed anchor nodes to connect to from*/
00965 /*      inside and outside, but are rather like the epsilon-transitions*/
00966 /*      in a (D,N)FA. Now is the time to get rid of them.*/
00967 /* */
00968 /*      We keep the .C'omments, and explicit .BRA'nches.*/
00969 /*      We should not have any .NOP which is a dead-end (without*/
00970 /*      successor), nor should we find .NOPs with more than one*/
00971 /*      successor. The latter should have been .BRA'nches. Both*/
00972 /*      situations are reported on. Dead-ends we*/
00973 /*      remove. Multi-destination NOPs we keep.*/
00974 /* */
00975 /*      Without the nops in place to confus the flow we can perform a*/
00976 /*      series peep-hole optimizations to merge/split branches.*/
00977 
00978 ret  ::page::compiler::peg::mecpu::denop (type g) {
00979     # Remove the .NOPs and reroute control flow. We keep the pseudo
00980     # instructions for comments (.C) and the explicit branch points
00981     # (.BRA).
00982 
00983     page_info "* Removing the helper .NOP instructions."
00984 
00985     set count 0
00986     foreach n [$g nodes] {
00987     # Skip over nodes already deleted by a previous iteration.
00988     if {[$g node get $n instruction] ne ".NOP"} continue
00989 
00990     # We keep branching .NOPs, and warn user. There shouldn't be
00991     # any. such should explicit bnrachpoints.
00992 
00993     set destinations [$g arcs -out $n]
00994 
00995     if {[llength $destinations] > 1} {
00996         page_error "$n ([printinsn $g $n])"
00997         page_error "Found a .NOP with more than one destination."
00998         page_error "This should have been a .BRA instruction."
00999         page_error "Not removed. Internal error. Fix the transformation."
01000         continue
01001     }
01002 
01003     # Nops without a destination, dead-end's are not wanted. They
01004     # should not exist either too. We will do a general dead-end
01005     # and dead-start removal as well.
01006 
01007     if {[llength $destinations] < 1} {
01008         page_error "$n ([printinsn $g $n])"
01009         page_error "Found a .NOP without any destination, i.e. a dead end."
01010         page_error "This should not have happened. Removed the node."
01011 
01012         $g node delete $n
01013         continue
01014     }
01015 
01016     page_log_info "    [np $n] removed, updated cflow ([printinsn $g $n])"
01017 
01018     # As there is exactly one destination we can now reroute all
01019     # incoming arcs around the nop to the new destination.
01020 
01021     set target [$g arc target [lindex $destinations 0]]
01022     foreach a [$g arcs -in $n] {
01023         $g arc move-target $a $target
01024     }
01025 
01026     $g node delete $n
01027     incr count
01028     }
01029 
01030     page_info "  Removed [plural $count instruction]"
01031     return
01032 }
01033 
01034 
01035 /*  ### ### ### ######### ######### #########*/
01036 /*  Optimizations.*/
01037 /* */
01038 
01039 /*  Merge parallel arcs (remove one, make the other unconditional).*/
01040 
01041 ret  ::page::compiler::peg::mecpu::parcmerge (type g) {
01042     page_info "* Search for identical parallel arcs and merge them"
01043 
01044     #puts [join  [info loaded] \n] /seg.fault induced with tcllibc! - tree!
01045 
01046     set count 0
01047     foreach n [$g nodes] {
01048     set arcs [$g arcs -out $n]
01049 
01050     if {[llength $arcs] < 2} continue
01051     if {[llength $arcs] > 2} {
01052         page_error "  $n ([printinsn $g $n])"
01053         page_error "  Instruction has more than two destinations."
01054         page_error "  That is not possible. Internal error."
01055         continue
01056     }
01057     # Two way branch. Both targets the same ?
01058 
01059     foreach {a b} $arcs break
01060 
01061     if {[$g arc target $a] ne [$g arc target $b]} continue
01062 
01063     page_log_info "    [np $n] outbound arcs merged ([printinsn $g $n])"
01064 
01065     $g arc set $a condition always
01066     $g arc delete $b
01067 
01068     incr count 2
01069     }
01070 
01071     page_info "  Merged [plural $count arc]"
01072     return
01073 }
01074 
01075 /*  Use knowledge of the match status before and after an instruction to*/
01076 /*  label the arcs a bit better (This may guide the forward and backward*/
01077 /*  merging.).*/
01078 
01079 /*  Forward merging of instructions.*/
01080 /*  An ok/fail decision is done as late as possible.*/
01081 /* */
01082 /*   /- ok ---> Y -> U               /- ok ---> U*/
01083 /*  X                    ==>   X -> Y*/
01084 /*   \- fail -> Y -> V               \- fail -> V*/
01085 
01086 /*  The Y must not have additional inputs. This more complex case we*/
01087 /*  will look at later.*/
01088 
01089 ret  ::page::compiler::peg::mecpu::forwmerge (type g) {
01090     page_info "* Forward merging of identical instructions"
01091     page_info "  Delaying decisions"
01092     set count 0
01093     set runs 0
01094 
01095     set merged 1
01096     while {$merged} {
01097     set merged 0
01098     foreach n [$g nodes] {
01099         # Skip nodes already killed in previous rounds.
01100         if {![$g node exists $n]} continue
01101 
01102         set outbound [$g arcs -out $n]
01103         if {[llength $outbound] != 2} continue
01104 
01105         foreach {aa ab} $outbound break
01106         set na [$g arc target $aa]
01107         set nb [$g arc target $ab]
01108 
01109         set ia [$g node get $na instruction][$g node get $na arguments]
01110         set ib [$g node get $nb instruction][$g node get $nb arguments]
01111         if {$ia ne $ib} continue
01112 
01113         # Additional condition: Inbounds in the targets not > 1
01114 
01115         if {([$g node degree -in $na] > 1) ||
01116         ([$g node degree -in $nb] > 1)} continue
01117 
01118         page_log_info "    /Merge [np $n] : [np $na] <- [np $nb] ([printinsn $g $na])"
01119 
01120         # Label all arcs out of na with the condition of the arc
01121         # into it.  Ditto for the arcs out of nb. The latter also
01122         # get na as their new origin. The arcs out of n relabeled
01123         # to always. The nb is deleted. This creates the desired
01124         # control structure without having to create a new node
01125         # and filling it. We simply use na, discard nb, and
01126         # properly rewrite the arcs to have the correct
01127         # conditions.
01128 
01129         foreach a [$g arcs -out $na] {
01130         $g arc set $a condition [$g arc get $aa condition]
01131         }
01132         foreach a [$g arcs -out $nb] {
01133         $g arc set $a condition [$g arc get $ab condition]
01134         $g arc move-source $a $na
01135         }
01136         $g arc set     $aa condition always
01137         $g node delete $nb
01138         set merged 1
01139         incr count
01140     }
01141     incr runs
01142     }
01143 
01144     # NOTE: This may require a parallel arc merge, with identification
01145     #       of merge-able arcs based on the arc condition, i.e. labeling.
01146 
01147     page_info "  Merged [plural $count instruction] in [plural $runs run]"
01148     return
01149 }
01150 
01151 /*  Backward merging of instructions.*/
01152 /*  Common backends are put together.*/
01153 /* */
01154 /*  U -> Y ->\             U ->\*/
01155 /*            -> X   ==>        -> Y -> X*/
01156 /*  V -> Y ->/             V ->/*/
01157 
01158 /*  Note. It is possible for an instruction to be amenable to both for-*/
01159 /*  and backward merging. No heuristics are known to decide which is*/
01160 /*  better.*/
01161 
01162 ret  ::page::compiler::peg::mecpu::backmerge (type g) {
01163     page_info "* Backward merging of identical instructions"
01164     page_info "  Unifying paths"
01165     set count 0
01166     set runs 0
01167 
01168     set merged 1
01169     while {$merged} {
01170     set merged 0
01171     foreach n [$g nodes] {
01172         # Skip nodes already killed in previous rounds.
01173         if {![$g node exists $n]} continue
01174 
01175         set inbound [$g arcs -in $n]
01176         if {[llength $inbound] < 2} continue
01177 
01178         # We have more than 1 inbound arcs on this node. Check all
01179         # pairs of pre-decessors for possible unification.
01180 
01181         # Additional condition: Outbounds in the targets not > 1
01182         # We check in different levels, to avoid redundant calls.
01183 
01184         while {[llength $inbound] > 2} {
01185         set aa   [lindex $inbound 0]
01186         set tail [lrange $inbound 1 end]
01187 
01188         set na [$g arc source $aa]
01189         if {[$g node degree -out $na] > 1} {
01190             set inbound $tail
01191             continue
01192         }
01193 
01194         set inbound {}
01195         foreach ab $tail {
01196             set nb [$g arc source $ab]
01197             if {[$g node degree -out $nb] > 1} continue
01198 
01199             set ia [$g node get $na instruction][$g node get $na arguments]
01200             set ib [$g node get $nb instruction][$g node get $nb arguments]
01201 
01202             if {$ia ne $ib} {
01203             lappend inbound $ab
01204             continue
01205             }
01206 
01207             page_log_info "    \\Merge [np $n] : [np $na] <- [np $nb] ([printinsn $g $na])"
01208 
01209             # Discard the second node in the pair. Move all
01210             # arcs inbound into it so that they reach the
01211             # first node instead.
01212 
01213             foreach a [$g arcs -in $nb] {$g arc move-target $a $na}
01214             $g node delete $nb
01215             set merged 1
01216             incr count
01217         }
01218         }
01219     }
01220     incr runs
01221     }
01222 
01223     page_info "  Merged [plural $count instruction] in [plural $runs run]"
01224     return
01225 }
01226 
01227 /*  ### ### ### ######### ######### #########*/
01228 
01229 ret  ::page::compiler::peg::mecpu::pathlengths (type g) {
01230     page_info "* Find maximum length paths"
01231 
01232     set pending [llength [$g nodes]]
01233 
01234     set nodes {}
01235     set loops {}
01236     foreach n [$g nodes] {
01237     $g node set $n WAIT [$g node degree -out $n]
01238     set insn [$g node get $n instruction]
01239     if {($insn eq "icf_halt") || ($insn eq "icf_ntreturn")} {
01240         lappend nodes $n
01241     }
01242     if {[$g node keyexists $n LOOP]} {
01243         lappend loops $n
01244     }
01245     }
01246 
01247     set level 0
01248     while {[llength $nodes]} {
01249     incr pending -[llength $nodes]
01250     set nodes [closure $g $nodes $level]
01251     incr level
01252     }
01253 
01254     if {[llength $loops]} {
01255     page_info "  Loop levels"
01256 
01257     set nodes $loops
01258     while {[llength $nodes]} {
01259         incr pending -[llength $nodes]
01260         set nodes [closure $g $nodes $level]
01261         incr level
01262     }
01263     }
01264 
01265     if {$pending} {
01266     page_info  "  Remainder"
01267 
01268     while {$pending} {
01269         set nodes {}
01270         foreach n [$g nodes] {
01271         if {[$g node keyexists $n LEVEL]} continue
01272         if {[$g node get $n WAIT] < [$g node degree -out $n]} {
01273             lappend nodes $n
01274         }
01275         }
01276         while {[llength $nodes]} {
01277         incr pending -[llength $nodes]
01278         set nodes [closure $g $nodes $level]
01279         incr level
01280         }
01281     }
01282     }
01283     return
01284 }
01285 
01286 ret  ::page::compiler::peg::mecpu::closure (type g , type nodes , type level) {
01287     page_log_info "  \[[format %6d $level]\] : $nodes"
01288 
01289     foreach n $nodes {$g node set $n LEVEL $level}
01290 
01291     set tmp {}
01292     foreach n $nodes {
01293     foreach pre [$g nodes -in $n] {
01294         # Ignore instructions already given a level.
01295         if {[$g node keyexists $pre LEVEL]} continue
01296         $g node set $pre WAIT [expr {[$g node get $pre WAIT] - 1}]
01297         if {[$g node get $pre WAIT] > 0} continue
01298         lappend tmp $pre
01299     }
01300     }
01301     return [lsort -uniq -dict $tmp]
01302 }
01303 
01304 ret  ::page::compiler::peg::mecpu::jumps (type g) {
01305     page_info "* Insert explicit jumps and branches"
01306 
01307     foreach n [$g nodes] {
01308     # Inbound > 1, at least one is from a jump, so a label is
01309     # needed.
01310 
01311     if {[llength [$g arcs -in $n]] > 1} {
01312         set go bra[string range $n 4 end]
01313         $g node set $n LABEL $go
01314     }
01315 
01316     set darcs [$g arcs -out $n]
01317 
01318     if {[llength $darcs] == 0} {
01319         $g node set $n NEXT ""
01320         continue
01321     }
01322 
01323     if {[llength $darcs] == 1} {
01324         set da [lindex $darcs 0]
01325         set dn [$g arc target $da]
01326 
01327         if {[$g node get $dn LEVEL] > [$g node get $n LEVEL]} {
01328         # Flow is backward, an uncond. jump
01329         # is needed here.
01330 
01331         set go bra[string range $dn 4 end]
01332         $g node set $dn LABEL $go
01333         set j [$g node insert]
01334         $g arc move-target $da $j
01335         $g node set $j instruction icf_jalways
01336         $g node set $j arguments   $go
01337 
01338         $g arc insert $j $dn
01339 
01340         $g node set $n NEXT $j
01341         $g node set $j NEXT ""
01342         } else {
01343         $g node set $n NEXT $dn
01344         }
01345         continue
01346     }
01347 
01348     set aok {}
01349     set afl {}
01350     foreach a $darcs {
01351         if {[$g arc get $a condition] eq "ok"} {
01352         set aok $a
01353         } else {
01354         set afl $a
01355         }
01356     }
01357     set nok [$g arc target $aok]
01358     set nfl [$g arc target $afl]
01359 
01360     if {[$g node get $n instruction] eq "inc_restore"} {
01361         set go bra[string range $nok 4 end]
01362         $g node set $nok LABEL $go
01363 
01364         $g node set $n NEXT $nfl
01365         $g node set $n SAVE $nok
01366 
01367         $g node set $n arguments [linsert [$g node get $n arguments] 0 $go]
01368         continue
01369     }
01370 
01371     if {[$g node get $n instruction] ne ".BRA"} {
01372         set bra [$g node insert]
01373         $g arc move-source $aok $bra
01374         $g arc move-source $afl $bra
01375         $g arc insert $n $bra
01376         $g node set $n NEXT $bra
01377         set n $bra
01378     }
01379 
01380     if {[$g node get $nok LEVEL] > [$g node get $nfl LEVEL]} {
01381         # Ok branch is direct, Fail is jump.
01382 
01383         $g node set $n NEXT $nok
01384         $g node set $n SAVE $nfl
01385 
01386         set go bra[string range $nfl 4 end]
01387         $g node set $nfl LABEL $go
01388         $g node set $n instruction icf_jfail
01389         $g node set $n arguments   $go
01390     } else {
01391 
01392         # Fail branch is direct, Ok is jump.
01393 
01394         $g node set $n NEXT $nfl
01395         $g node set $n SAVE $nok
01396 
01397         set go bra[string range $nok 4 end]
01398         $g node set $nok LABEL $go
01399         $g node set $n instruction icf_jok
01400         $g node set $n arguments   $go
01401     }
01402     }
01403 }
01404 
01405 ret  ::page::compiler::peg::mecpu::symbols (type g , type t) {
01406     page_info "* Label subroutine heads"
01407 
01408     # Label and mark the instructions where subroutines begin.
01409     # These markers are used by 2code to locate all actually
01410     # used subroutines.
01411 
01412     foreach def [lsort -uniq [$t get root gas::called]] {
01413     set gdef [$t get $def gas::entry]
01414     foreach caller [$t get $def gas::callers] {
01415 
01416         # Skip callers which are gone because of optimizations.
01417         if {![$g node exists $caller]} continue
01418 
01419         $g node set $caller CALL $gdef
01420         $g node set $gdef LABEL \
01421             [lindex [$g node set $caller arguments] 0]
01422     }
01423     }
01424     return
01425 }
01426 
01427 /*  ### ### ### ######### ######### #########*/
01428 
01429 ret  ::page::compiler::peg::mecpu::statistics (type code) {
01430     return
01431     # disabled
01432     page_info "* Statistics"
01433     statistics_si $code
01434 
01435     # All higher order statistics are done only on the instructions in
01436     # a basic block, i.e. a linear sequence. We are looking for
01437     # high-probability blocks in itself, and then also for
01438     # high-probability partials.
01439 
01440     set blocks [basicblocks $code]
01441 
01442     # Basic basic block statistics (full blocks)
01443 
01444     Init bl
01445     foreach b $blocks {Incr bl($b)}
01446     wrstat  bl asm/statistics_bb.txt
01447     wrstatk bl asm/statistics_bbk.txt
01448 
01449     # Statistics of all partial blocks, i.e. all possible
01450     # sub-sequences with length > 1.
01451 
01452     Init ps
01453     foreach b $blocks {
01454     for {set s 0} {$s < [llength $b]} {incr s} {
01455         for {set e [expr {$s + 1}]} {$e < [llength $b]} {incr e} {
01456         Incr ps([lrange $b $s $e]) $bl($b)
01457         }
01458     }
01459     }
01460 
01461     wrstat  ps asm/statistics_ps.txt
01462     wrstatk ps asm/statistics_psk.txt
01463     return
01464 }
01465 
01466 ret  ::page::compiler::peg::mecpu::statistics_si (type code) {
01467     page_info "  Single instruction probabilities."
01468 
01469     # What are the most used instructions, statically speaking,
01470     # without considering context ?
01471 
01472     Init si
01473     foreach i $code {
01474     foreach {label name} $i break
01475     if {$name eq ".C"} continue
01476     Incr si($name)
01477     }
01478 
01479     wrstat si asm/statistics_si.txt
01480     return
01481 }
01482 
01483 ret  ::page::compiler::peg::mecpu::Init (type v) {
01484     upvar 1 $v var total total
01485     array set var {}
01486     set total 0
01487     return
01488 }
01489 
01490 ret  ::page::compiler::peg::mecpu::Incr (type v , optional n =1) {
01491     upvar 1 $v var total total
01492     if {![info exists var]} {set var $n ; incr total ; return}
01493     incr var $n
01494     incr total $n
01495     return
01496 }
01497 
01498 ret  ::page::compiler::peg::mecpu::wrstat (type bv , type file) {
01499     upvar 1 $bv buckets total total
01500 
01501     set tmp  {}
01502     foreach {name count} [array get buckets] {
01503     lappend tmp [list $name $count]
01504     }
01505 
01506     set     lines {}
01507     lappend lines "Total: $total"
01508 
01509     set half [expr {$total / 2}]
01510     set down $total
01511 
01512     foreach item [lsort -index 1 -decreasing -integer [lsort -index 0 $tmp]] {
01513     foreach {key count} $item break
01514 
01515     set percent [format %6.2f [expr {$count*100.0/$total}]]%
01516     set fcount  [format %8d $count]
01517 
01518     lappend lines "  $fcount $percent $key"
01519     incr down -$count
01520     if {$half && ($down < $half)} {
01521         lappend lines **
01522         set half 0
01523     }
01524     }
01525 
01526     write $file [join $lines \n]\n
01527     return
01528 }
01529 
01530 ret  ::page::compiler::peg::mecpu::wrstatk (type bv , type file) {
01531     upvar 1 $bv buckets total total
01532 
01533     set tmp  {}
01534     foreach {name count} [array get buckets] {
01535     lappend tmp [list $name $count]
01536     }
01537 
01538     set     lines {}
01539     lappend lines "Total: $total"
01540 
01541     set half [expr {$total / 2}]
01542     set down $total
01543 
01544     foreach item  [lsort -index 0 [lsort -index 1 -decreasing -integer $tmp]] {
01545     foreach {key count} $item break
01546 
01547     set percent [format %6.2f [expr {$count*100.0/$total}]]%
01548     set fcount  [format %8d $count]
01549 
01550     lappend lines "  $fcount $percent $key"
01551     incr down -$count
01552     if {$down < $half} {
01553         lappend lines **
01554         set half -1
01555     }
01556     }
01557 
01558     write $file [join $lines \n]\n
01559     return
01560 }
01561 
01562 ret  ::page::compiler::peg::mecpu::basicblocks (type code) {
01563     set blocks {}
01564     set block {}
01565 
01566     foreach i $code {
01567     foreach {label name} $i break
01568     if {
01569         ($name eq ".C")          ||
01570         ($name eq "icf_jok")     ||
01571         ($name eq "icf_jfail")   ||
01572         ($name eq "icf_jalways") ||
01573         ($name eq "icf_ntreturn")
01574     } {
01575         # Jumps stop a block, and are not put into the block
01576         # Except if the block is of length 1. Then it is of
01577         # interest to see if certain combinations are used
01578         # often.
01579 
01580         if {[llength $block]} {
01581         if {[llength $block] == 1} {lappend block $name}
01582         lappend blocks $block
01583         }
01584         set block {}
01585         continue
01586     } elseif {$label ne ""} {
01587         # A labeled instruction starts a new block and belongs to
01588         # it. Note that the previous block is saved only if it is
01589         # of length > 1. A single instruction block is not
01590         # something we can optimize.
01591 
01592         if {[llength $block] > 1} {lappend blocks $block}
01593         set block [list $name]
01594         continue
01595     }
01596     # Extend current block
01597     lappend block $name
01598     }
01599 
01600     if {[llength $block]} {lappend blocks $block}
01601     return $blocks
01602 }
01603 
01604 /*  ### ### ### ######### ######### #########*/
01605 
01606 ret  ::page::compiler::peg::mecpu::printinsn (type g , type n) {
01607     return "[$g node get $n instruction] <[$g node get $n arguments]>"
01608 }
01609 
01610 ret  ::page::compiler::peg::mecpu::plural (type n , type prefix) {
01611     return "$n ${prefix}[expr {$n == 1 ? "" : "s"}]"
01612 }
01613 
01614 ret  ::page::compiler::peg::mecpu::np (type n) {
01615     format %-*s 8 $n
01616 }
01617 
01618 ret  ::page::compiler::peg::mecpu::status (type g) {
01619     page_info "[plural [llength [$g nodes]] instruction]"
01620     return
01621 }
01622 
01623 ret  ::page::compiler::peg::mecpu::gdump (type g , type file) {
01624     return
01625     # disabled
01626     variable gnext
01627     page_info "  %% Saving graph to \"$file\" %%"
01628     write asm/[format %02d $gnext]_${file}.sgr [$g serialize]
01629     incr gnext
01630     return
01631 }
01632 
01633 /*  ### ### ### ######### ######### #########*/
01634 /*  Internal. Strings.*/
01635 
01636 namespace ::page::compiler::peg::mecpu {
01637     variable gnext 0
01638 }
01639 
01640 /*  ### ### ### ######### ######### #########*/
01641 /*  Ready*/
01642 
01643 package provide page::compiler::peg::mecpu 0.1.1
01644 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1