2 ; Copyright (C) 2000, 2009 Red Hat, Inc.
3 ; This file is part of CGEN.
4 ; See file COPYING.CGEN for details.
7 ; Some improvement in pbb simulator efficiency is obtained in cases like
8 ; the ARM where for example operand2 computation is expensive in terms of
9 ; cpu cost, code size, and subroutine call overhead if the code is put in
10 ; a subroutine. It could be inlined, but there are numerous occurences
11 ; resulting in poor icache usage.
12 ; If the computation is put in its own fragment then code size is reduced
13 ; [improving icache usage] and subroutine call overhead is removed in a
14 ; computed-goto simulator [arguments are passed in machine generated local
17 ; The basic procedure here is to:
18 ; - break all insns up into a set of statements
19 ; This is either one statement in the case of insns that don't begin with a
20 ; sequence, or a list of statements, one for each element in the sequence.
21 ; - find a profitable set of common leading statements (called the "header")
22 ; and a profitable set of common trailing statements (called the "trailer")
23 ; What is "profitable" depends on
24 ; - how expensive the statement is
25 ; - how long the statement is
26 ; - the number of insns using the statement
27 ; - what fraction of the total insn the statement is
28 ; - rewrite insn semantics in terms of the new header and trailer fragments
29 ; plus a "middle" part that is whatever is left over
30 ; - there is always a header, the middle and trailer parts are optional
31 ; - cti insns require a header and trailer, though they can be the same
35 ; - check ARM orr insns which come out as header, tiny middle, trailer
36 ; - the tiny middle seems like a waste (combine with trailer?)
37 ; - there are 8 trailers consisting of just `nop' for ARM
38 ; - rearranging statements to increase number and length of common sets
39 ; - combine common middle fragments
40 ; - parallel's not handled yet (only have to handle parallel's at the
42 ; - insns can also be split on timing-sensitive boundaries (pipeline, memory,
43 ; whatever) though that is not implemented yet. This may involve rtl
47 ; - call sim-sfrag-init! first, to initialize
48 ; - call sim-sfrag-analyze-insns! to create the semantic fragments
50 ; - sim-sfrag-insn-list
51 ; - sim-sfrag-frag-table
52 ; - sim-sfrag-usage-table
53 ; - sim-sfrag-locals-list
55 ; Statement computation.
57 ; Set to #t to collect various statistics.
59 (define /stmt-stats? #f)
61 ; Collection of computed stats. Only set if /stmt-stats? = #t.
63 (define /stmt-stats #f)
65 ; Collection of computed statement data. Only set if /stmt-stats? = #t.
67 (define /stmt-stats-data #f)
69 ; Create a structure recording data of all statements.
70 ; A pair of (next-ordinal . table).
72 (define (/stmt-data-make hash-size)
73 (cons 0 (make-vector hash-size nil))
78 (define (/stmt-data-table data) (cdr data))
79 (define (/stmt-data-next-num data) (car data))
80 (define (/stmt-data-set-next-num! data newval) (set-car! data newval))
81 (define (/stmt-data-hash-size data) (vector-length (cdr data)))
84 ; INSN semantics either consist of a single statement or a sequence of them.
87 (class-make '<statement> nil
92 ; Local variables of the sequence `expr' is in.
93 ; This is recorded in the same form as the sequence,
97 ; Ordinal of the statement.
101 ; SPEED-COST is the cost of executing fragment, relative to a
103 ; SIZE-COST is the size of the fragment, relative to a simple
105 ; ??? The cost numbers are somewhat arbitrary and subject to
110 ; Users of this statement.
111 ; Each element is (owner-number . owner-object),
112 ; where owner-number is an index into the initial insn table
113 ; (e.g. insn-list arg of /sfrag-create-cse-mapping), and
114 ; owner-object is the corresponding object.
120 (define-getters <statement> -stmt (expr locals num speed-cost size-cost users))
122 (define-setters <statement> -stmt (users))
124 ; Make a <statement> object of EXPR.
125 ; LOCALS is a list of local variables of the sequence EXPR is in.
126 ; NUM is the ordinal of EXPR.
127 ; SPEED-COST is the cost of executing the statement, relative to a simple add.
128 ; SIZE-COST is the size of the fragment, relative to a simple add.
129 ; ??? The cost numbers are somewhat arbitrary and subject to review.
131 ; The user list is set to nil.
133 (define (/stmt-make expr locals num speed-cost size-cost)
134 (make <statement> expr locals num speed-cost size-cost nil)
137 ; Add a user of STMT.
139 (define (/stmt-add-user! stmt user-num user-obj)
140 (-stmt-set-users! stmt (cons (cons user-num user-obj) (-stmt-users stmt)))
144 ; Lookup STMT in DATA.
145 ; CHAIN-NUM is an argument so it need only be computed once.
146 ; The result is the found <statement> object or #f.
148 (define (/frag-lookup-stmt data chain-num stmt)
149 (let ((table (/stmt-data-table data)))
150 (let loop ((stmts (vector-ref table chain-num)))
153 ; ??? equal? should be appropriate rtx-equal?, blah blah blah.
154 ((equal? (-stmt-expr (car stmts)) stmt)
157 (loop (cdr stmts))))))
162 ; Computed hash value.
163 ; Global 'cus /frag-hash-compute! is defined globally so we can use
164 ; /fastcall (FIXME: Need /fastcall to work on non-global procs).
166 (define /frag-hash-value-tmp 0)
168 (define (/frag-hash-string str)
169 (let loop ((chars (map char->integer (string->list str))) (result 0))
172 (loop (cdr chars) (modulo (+ (* result 7) (car chars)) #xfffffff))))
175 ;; MODE is the name of the mode.
177 (define (/frag-hash-compute! rtx-obj expr mode parent-expr op-pos tstate appstuff)
179 (case (rtx-name expr)
181 (set! h (/frag-hash-string (symbol->string (rtx-operand-name expr)))))
183 (set! h (/frag-hash-string (symbol->string (rtx-local-name expr)))))
185 (set! h (rtx-const-value expr)))
187 (set! h (rtx-num rtx-obj))))
188 (set! /frag-hash-value-tmp
190 (modulo (+ (* /frag-hash-value-tmp 3) h op-pos)
193 ; #f -> "continue with normal traversing"
197 (define (/frag-hash-stmt stmt locals size)
198 (set! /frag-hash-value-tmp 0)
199 (rtx-traverse-with-locals #f #f stmt /frag-hash-compute! locals #f) ; FIXME: (/fastcall-make /frag-hash-compute!))
200 (modulo /frag-hash-value-tmp size)
203 ; Compute the speed/size costs of a statement.
205 ; Compute speed/size costs.
206 ; Global 'cus /frag-cost-compute! is defined globally so we can use
207 ; /fastcall (FIXME: Need /fastcall to work on non-global procs).
209 (define /frag-speed-cost-tmp 0)
210 (define /frag-size-cost-tmp 0)
212 ;; MODE is the name of the mode.
214 (define (/frag-cost-compute! rtx-obj expr mode parent-expr op-pos tstate appstuff)
218 (case (rtx-class rtx-obj)
220 #f) ; these don't contribute to costs (at least for now)
222 ; FIXME: speed/size = 0?
225 ((UNARY BINARY TRINARY)
234 (set! /frag-speed-cost-tmp (+ /frag-speed-cost-tmp speed))
235 (set! /frag-size-cost-tmp (+ /frag-size-cost-tmp size)))
237 ; #f -> "continue with normal traversing"
241 (define (/frag-stmt-cost stmt locals)
242 (set! /frag-speed-cost-tmp 0)
243 (set! /frag-size-cost-tmp 0)
244 (rtx-traverse-with-locals #f #f stmt /frag-cost-compute! locals #f) ; FIXME: (/fastcall-make /frag-cost-compute!))
245 (cons /frag-speed-cost-tmp /frag-size-cost-tmp)
248 ; Add STMT to statement table DATA.
249 ; CHAIN-NUM is the chain in the hash table to add STMT to.
250 ; {SPEED,SIZE}-COST are passed through to /stmt-make.
251 ; The result is the newly created <statement> object.
253 (define (/frag-add-stmt! data chain-num stmt locals speed-cost size-cost)
254 (let ((stmt (/stmt-make stmt locals (/stmt-data-next-num data) speed-cost size-cost))
255 (table (/stmt-data-table data)))
256 (vector-set! table chain-num (cons stmt (vector-ref table chain-num)))
257 (/stmt-data-set-next-num! data (+ 1 (/stmt-data-next-num data)))
261 ; Return the locals in EXPR.
262 ; If a sequence, return locals.
263 ; Otherwise, return nil.
264 ; The result is in assq'able form.
266 (define (/frag-expr-locals expr)
267 (if (rtx-kind? 'sequence expr)
268 (rtx-sequence-locals expr)
272 ; Return the locals in EXPR in assq-able form, i.e. (name MODE).
273 ; If a sequence, return locals.
274 ; Otherwise, return nil.
275 ; The result is in assq'able form.
277 (define (/frag-expr-assq-locals expr)
278 (if (rtx-kind? 'sequence expr)
279 (rtx-sequence-assq-locals expr)
283 ; Return the statements in EXPR.
284 ; If a sequence, return the sequence's expressions.
285 ; Otherwise, return (list expr).
287 (define (/frag-expr-stmts expr)
288 (if (rtx-kind? 'sequence expr)
289 (rtx-sequence-exprs expr)
293 ; Analyze statement STMT.
294 ; If STMT is already in STMT-DATA increment its frequency count.
296 ; LOCALS are locals of the sequence STMT is in.
297 ; USAGE-TABLE is a vector of statement index lists for each expression.
298 ; USAGE-INDEX is the index of USAGE-TABLE to use.
299 ; OWNER is the object of the owner of the statement.
301 (define (/frag-analyze-expr-stmt! locals stmt stmt-data usage-table expr-num owner)
302 (logit 3 "Analyzing statement: " (rtx-strdump stmt) "\n")
304 (/frag-hash-stmt stmt locals (/stmt-data-hash-size stmt-data)))
305 (stmt-obj (/frag-lookup-stmt stmt-data chain-num stmt)))
307 (logit 3 " chain #" chain-num "\n")
310 (let* ((costs (/frag-stmt-cost stmt locals))
311 (speed-cost (car costs))
312 (size-cost (cdr costs)))
313 (set! stmt-obj (/frag-add-stmt! stmt-data chain-num stmt locals
314 speed-cost size-cost))
315 (logit 3 " new statement, #" (-stmt-num stmt-obj) "\n"))
316 (logit 3 " existing statement, #" (-stmt-num stmt-obj) "\n"))
318 (/stmt-add-user! stmt-obj expr-num owner)
320 ; If first entry, initialize list, otherwise append to existing list.
321 (if (null? (vector-ref usage-table expr-num))
322 (vector-set! usage-table expr-num (list (-stmt-num stmt-obj)))
323 (append! (vector-ref usage-table expr-num)
324 (list (-stmt-num stmt-obj)))))
329 ; Analyze each statement in EXPR and add it to STMT-DATA.
330 ; OWNER is the object of the owner of the expression.
331 ; USAGE-TABLE is a vector of statement index lists for each expression.
332 ; USAGE-INDEX is the index of the USAGE-TABLE entry to use.
333 ; As each statement's ordinal is computed it is added to the usage list.
335 (define (/frag-analyze-expr! expr owner stmt-data usage-table usage-index)
336 (logit 3 "Analyzing " (obj:name owner) ": " (rtx-strdump expr) "\n")
337 (let ((locals (/frag-expr-locals expr))
338 (stmt-list (/frag-expr-stmts expr)))
339 (for-each (lambda (stmt)
340 (/frag-analyze-expr-stmt! locals stmt stmt-data
341 usage-table usage-index owner))
346 ; Compute statement data from EXPRS, a list of expressions.
347 ; OWNERS is a vector of objects that "own" each corresponding element in EXPRS.
348 ; The owner is usually an <insn> object. Actually it'll probably always be
349 ; an <insn> object but for now I want the disassociation.
351 ; The result contains:
352 ; - vector of statement lists of each expression
353 ; - each element is (stmt1-index stmt2-index ...) where each stmtN-index is
354 ; an index into the statement table
355 ; - vector of statements (the statement table of the previous item)
356 ; - each element is a <statement> object
358 (define (/frag-compute-statements exprs owners)
359 (logit 2 "Computing statement table ...\n")
360 (let* ((num-exprs (length exprs))
362 ; FIXME: This is just a quick hack to put something down on paper.
363 ; blah blah blah. Revisit as necessary.
364 (cond ((> num-exprs 300) 1019)
365 ((> num-exprs 100) 511)
368 (let (; Hash table of expressions.
369 (stmt-data (/stmt-data-make hash-size))
370 ; Statement index lists for each expression.
371 (usage-table (make-vector num-exprs nil)))
373 ; Scan each expr, filling in stmt-data and usage-table.
374 (let loop ((exprs exprs) (exprnum 0))
375 (if (not (null? exprs))
376 (let ((expr (car exprs))
377 (owner (vector-ref owners exprnum)))
378 (/frag-analyze-expr! expr owner stmt-data usage-table exprnum)
379 (loop (cdr exprs) (+ exprnum 1)))))
381 ; Convert statement hash table to vector.
382 (let ((stmt-hash-table (/stmt-data-table stmt-data))
383 (end (vector-length (/stmt-data-table stmt-data)))
384 (stmt-table (make-vector (/stmt-data-next-num stmt-data) #f)))
389 (vector-set! stmt-table (-stmt-num stmt) stmt))
390 (vector-ref stmt-hash-table i))
393 ; All done. Compute stats if asked to.
396 ; See how well the hashing worked.
397 (set! /stmt-stats-data stmt-data)
399 (make-vector (vector-length stmt-hash-table) #f))
403 (vector-set! /stmt-stats i
404 (length (vector-ref stmt-hash-table i)))
408 (cons usage-table stmt-table))))
411 ; Semantic fragment selection.
413 ; "semantic fragment" is the name assigned to each header/middle/trailer
414 ; "fragment" as each may consist of more than one statement, though not
415 ; necessarily all statements of the original sequence.
418 (class-make '<sfrag> '(<ident>)
420 ; List of insn's using this frag.
423 ; Ordinal's of each element of `users'.
426 ; Semantic format of insns using this fragment.
429 ; List of statement numbers that make up `semantics'.
430 ; Each element is an index into the stmt-table arg of
432 ; This is #f if the sfrag wasn't derived from some set of
436 ; Raw rtl source of fragment.
442 ; Boolean indicating if this frag is for parallel exec support.
445 ; Boolean indicating if this is a header frag.
446 ; This includes all frags that begin a sequence.
449 ; Boolean indicating if this is a trailer frag.
450 ; This includes all frags that end a sequence.
456 (define-getters <sfrag> sfrag
457 (users user-nums sfmt stmt-numbers semantics compiled-semantics
458 parallel? header? trailer?)
461 (define-setters <sfrag> sfrag
465 ; Sorter to merge common fragments together.
466 ; A and B are lists of statement numbers.
468 (define (/frag-sort a b)
478 (/frag-sort (cdr a) (cdr b))))
481 ; Return a boolean indicating if L1,L2 match in the first LEN elements.
482 ; Each element is an integer.
484 (define (/frag-list-match? l1 l2 len)
487 ((or (null? l1) (null? l2))
489 ((= (car l1) (car l2))
490 (/frag-list-match? (cdr l1) (cdr l2) (- len 1)))
495 ; Return the number of expressions that match in the first LEN statements.
497 (define (/frag-find-matching expr-table indices stmt-list len)
498 (let loop ((num-exprs 0) (indices indices))
499 (cond ((null? indices)
501 ((/frag-list-match? stmt-list
502 (vector-ref expr-table (car indices)) len)
503 (loop (+ num-exprs 1) (cdr indices)))
508 ; Return a boolean indicating if making STMT-LIST a common fragment
509 ; among several owners is profitable.
510 ; STMT-LIST is a list of statement numbers, indices into STMT-TABLE.
511 ; NUM-EXPRS is the number of expressions with STMT-LIST in common.
513 (define (/frag-merge-profitable? stmt-table stmt-list num-exprs)
515 (and (>= num-exprs 2)
516 (or ; No need to include speed costs yet.
517 ;(>= (/frag-list-speed-cost stmt-table stmt-list) 10)
518 (>= (/frag-list-size-cost stmt-table stmt-list) 4)))
521 ; Return the cost of executing STMT-LIST.
522 ; STMT-LIST is a list of statment numbers, indices into STMT-TABLE.
524 ; FIXME: The yardstick to use is wip. Currently we measure things relative
525 ; to a simple add insn which is given the value 1.
527 (define (/frag-list-speed-cost stmt-table stmt-list)
529 (apply + (map (lambda (stmt-num)
530 (-stmt-speed-cost (vector-ref stmt-table stmt-num)))
534 (define (/frag-list-size-cost stmt-table stmt-list)
536 (apply + (map (lambda (stmt-num)
537 (-stmt-size-cost (vector-ref stmt-table stmt-num)))
541 ; Compute the longest set of fragments it is desirable/profitable to create.
542 ; The result is (number-of-matching-exprs . stmt-number-list)
543 ; or #f if there isn't one (the longest set is the empty set).
545 ; What is desirable depends on a few things:
546 ; - how often is it used?
547 ; - how expensive is it (size-wise and speed-wise)
548 ; - relationship to other frags
550 ; STMT-TABLE is a vector of all statements.
551 ; STMT-USAGE-TABLE is a vector of all expressions. Each element is a list of
552 ; statement numbers (indices into STMT-TABLE).
553 ; INDICES is a sorted list of indices into STMT-USAGE-TABLE.
554 ; STMT-USAGE-TABLE is processed in the order specified by INDICES.
556 ; FIXME: Choosing a statement list should depend on whether there are existing
557 ; chosen statement lists only slightly shorter.
559 (define (/frag-longest-desired stmt-table stmt-usage-table indices)
560 ; STMT-LIST is the list of statements in the first expression.
561 (let ((stmt-list (vector-ref stmt-usage-table (car indices))))
563 (let loop ((len 1) (prev-num-exprs 0))
565 ; See how many subsequent expressions match at length LEN.
566 (let ((num-exprs (/frag-find-matching stmt-usage-table (cdr indices)
568 ; If there aren't any, we're done.
569 ; If LEN-1 is usable, return that.
570 ; Otherwise there is no profitable list of fragments.
573 (let ((matching-stmt-list (list-take (- len 1) stmt-list)))
574 (if (/frag-merge-profitable? stmt-table matching-stmt-list
576 (cons prev-num-exprs matching-stmt-list)
579 ; Found at least 1 subsequent matching expression.
580 ; Extend LEN and see if we still find matching expressions.
581 (loop (+ len 1) num-exprs)))))
584 ; Return list of lists of objects for each unique <sformat-argbuf> in
586 ; Each element of USER-LIST is (insn-num . <insn> object).
587 ; The result is a list of lists. Each element in the top level list is
588 ; a list of elements of USER-LIST that have the same <sformat-argbuf>.
589 ; Insns are also distinguished by being a CTI insn vs a non-CTI insn.
590 ; CTI insns require special handling in the semantics.
592 (define (/frag-split-by-sbuf user-list)
594 (if (not (elm-bound? (cdar user-list) 'sfmt))
595 (error "sformats not computed"))
596 (if (not (elm-bound? (insn-sfmt (cdar user-list)) 'sbuf))
597 (error "sformat argbufs not computed"))
600 ; Find INSN in SFMT-LIST. The result is the list INSN belongs in
602 (find-obj (lambda (sbuf-list insn)
603 (let ((name (obj:name (sfmt-sbuf (insn-sfmt insn)))))
604 (let loop ((sbuf-list sbuf-list))
605 (cond ((null? sbuf-list)
608 (obj:name (sfmt-sbuf (insn-sfmt (cdaar sbuf-list)))))
609 (eq? (insn-cti? insn)
610 (insn-cti? (cdaar sbuf-list))))
613 (loop (cdr sbuf-list))))))))
615 (let loop ((users user-list))
616 (if (not (null? users))
617 (let ((try (find-obj result (cdar users))))
619 (append! try (list (car users)))
620 (set! result (cons (list (car users)) result)))
621 (loop (cdr users)))))
627 ; Return a list of desired fragments to create.
628 ; These consist of the longest set of profitable leading statements in EXPRS.
629 ; Each element of the result is an <sfrag> object.
631 ; STMT-TABLE is a vector of all statements.
632 ; STMT-USAGE-TABLE is a vector of statement number lists of each expression.
633 ; OWNER-TABLE is a vector of owner objects of each corresponding expression
634 ; in STMT-USAGE-TABLE.
635 ; KIND is one of 'header or 'trailer.
637 ; This works for trailing fragments too as we do the computation based on the
638 ; reversed statement lists.
640 (define (/frag-compute-desired-frags stmt-table stmt-usage-table owner-table kind)
641 (logit 2 "Computing desired " kind " frags ...\n")
645 (if (eq? kind 'header)
646 (vector->list stmt-usage-table)
647 (map reverse (vector->list stmt-usage-table))))
648 ; Sort STMT-USAGE-TABLE. That will bring exprs with common fragments
650 (sorted-indices (sort-grade stmt-usage-list /frag-sort))
651 ; List of statement lists that together yield the fragment to create,
652 ; plus associated users.
656 ; Update STMT-USAGE-TABLE in case we reversed the contents.
657 (set! stmt-usage-table (list->vector stmt-usage-list))
659 (let loop ((indices sorted-indices) (iteration 1))
660 (logit 3 "Iteration " iteration "\n")
661 (if (not (null? indices))
662 (let ((longest (/frag-longest-desired stmt-table stmt-usage-table indices)))
666 ; Found an acceptable frag to create.
667 (let* ((num-exprs (car longest))
668 ; Reverse statement numbers back if trailer.
669 (stmt-list (if (eq? kind 'header)
671 (reverse (cdr longest))))
672 (picked-indices (list-take num-exprs indices))
673 ; Need one copy of the frag for each sbuf, as structure
674 ; offsets will be different in generated C/C++ code.
675 (sfmt-users (/frag-split-by-sbuf
676 (map (lambda (expr-num)
678 (vector-ref owner-table
682 (logit 3 "Creating frag of length " (length stmt-list) ", " num-exprs " users\n")
683 (logit 3 "Indices: " picked-indices "\n")
685 ; Create an sfrag for each sbuf.
688 (let* ((first-owner (cdar users))
691 (symbol-append (obj:name first-owner)
692 (if (eq? kind 'header)
699 (insn-sfmt first-owner)
706 (map (lambda (stmt-num)
708 (vector-ref stmt-table
711 #f ; compiled-semantics
716 (set! desired-frags (cons sfrag desired-frags))))
719 ; Continue, dropping statements we've put into the frag.
720 (loop (list-drop num-exprs indices) (+ iteration 1)))
722 ; Couldn't find an acceptable statement list.
723 ; Try again with next one.
725 (logit 3 "No acceptable frag found.\n")
726 (loop (cdr indices) (+ iteration 1)))))))
732 ; Return the set of desired fragments to create.
733 ; STMT-TABLE is a vector of each statement.
734 ; STMT-USAGE-TABLE is a vector of (stmt1-index stmt2-index ...) elements for
735 ; each expression, where each stmtN-index is an index into STMT-TABLE.
736 ; OWNER-TABLE is a vector of owner objects of each corresponding expression
737 ; in STMT-USAGE-TABLE.
739 ; Each expression is split in up to three pieces: header, middle, trailer.
740 ; This computes pseudo-optimal headers and trailers (if they exist).
741 ; The "middle" part is whatever is leftover.
743 ; The result is a vector of 4 elements:
744 ; - vector of (header middle trailer) semantic fragments for each expression
745 ; - each element is an index into the respective table or #f if not present
746 ; - list of header fragments, each element is an <sfrag> object
747 ; - same but for trailer fragments
748 ; - same but for middle fragments
750 ; ??? While this is a big function, each piece is simple and straightforward.
751 ; It's kept as one big function so we can compute each expression's sfrag list
752 ; as we go. Though it's not much extra expense to not do this.
754 (define (/frag-pick-best stmt-table stmt-usage-table owner-table)
756 (num-stmts (vector-length stmt-table))
757 (num-exprs (vector-length stmt-usage-table))
758 ; FIXME: Shouldn't have to do vector->list.
759 (stmt-usage-list (vector->list stmt-usage-table))
760 ; Specify result holders here, simplifies code.
761 (desired-header-frags #f)
762 (desired-trailer-frags #f)
764 ; Also allocate space for expression sfrag usage table.
765 ; We compute it as we go to save scanning the header and trailer
767 ; copy-tree is needed to avoid shared storage.
768 (expr-sfrags (copy-tree (make-vector (vector-length stmt-usage-table)
772 ; Compute desired headers.
773 (set! desired-header-frags
774 (/frag-compute-desired-frags stmt-table stmt-usage-table owner-table
777 ; Compute the header used by each expression.
778 (let ((expr-hdrs-v (make-vector num-exprs #f))
779 (num-hdrs (length desired-header-frags)))
780 (let loop ((hdrs desired-header-frags) (hdrnum 0))
781 (if (< hdrnum num-hdrs)
782 (let ((hdr (car hdrs)))
783 (for-each (lambda (expr-num)
784 (vector-set! (vector-ref expr-sfrags expr-num) 0
786 (vector-set! expr-hdrs-v expr-num hdr))
787 (sfrag-user-nums hdr))
788 (loop (cdr hdrs) (+ hdrnum 1)))))
790 ; Truncate each expression by the header it will use and then find
791 ; the set of desired trailers.
792 (let ((expr-hdrs (vector->list expr-hdrs-v)))
794 (set! desired-trailer-frags
795 (/frag-compute-desired-frags
797 ; FIXME: Shouldn't have to use list->vector.
798 ; [still pass a vector, but use vector-map here instead of map]
800 (map (lambda (expr hdr)
802 (list-drop (length (sfrag-stmt-numbers hdr)) expr)
804 stmt-usage-list expr-hdrs))
808 ; Record the trailer used by each expression.
809 (let ((expr-trlrs-v (make-vector num-exprs #f))
810 (num-trlrs (length desired-trailer-frags)))
811 (let loop ((trlrs desired-trailer-frags) (trlrnum 0))
812 (if (< trlrnum num-trlrs)
813 (let ((trlr (car trlrs)))
814 (for-each (lambda (expr-num)
815 (vector-set! (vector-ref expr-sfrags expr-num) 2
817 (vector-set! expr-trlrs-v expr-num trlr))
818 (sfrag-user-nums trlr))
819 (loop (cdr trlrs) (+ trlrnum 1)))))
821 ; We have the desired headers and trailers, now compute the middle
822 ; part for each expression. This is just what's left over.
823 ; ??? We don't try to cse the middle part. Though we can in the
824 ; future should it prove useful enough.
825 (logit 2 "Computing middle frags ...\n")
826 (let* ((expr-trlrs (vector->list expr-trlrs-v))
828 (map (lambda (expr hdr trlr)
830 (if trlr (length (sfrag-stmt-numbers trlr)) 0)
832 (if hdr (length (sfrag-stmt-numbers hdr)) 0)
834 stmt-usage-list expr-hdrs expr-trlrs)))
836 ; Finally, record the middle sfrags used by each expression.
837 (let loop ((tmp-middle-frags nil)
838 (next-middle-frag-num 0)
840 (expr-middle-stmts expr-middle-stmts))
842 (if (null? expr-middle-stmts)
845 ; [The next statement executed after this is the one at the
846 ; end that builds the result. Maybe it should be built here
847 ; and this should be the last statement, but I'm trying this
848 ; style out for awhile.]
849 (set! middle-frags (reverse! tmp-middle-frags))
851 ; Does this expr have a middle sfrag?
852 (if (null? (car expr-middle-stmts))
854 (loop tmp-middle-frags
857 (cdr expr-middle-stmts))
859 (let ((owner (vector-ref owner-table expr-num)))
860 (vector-set! (vector-ref expr-sfrags expr-num)
861 1 next-middle-frag-num)
862 (loop (cons (make <sfrag>
863 (symbol-append (obj:name owner) '-mid)
864 (string-append (obj:comment owner)
870 (car expr-middle-stmts)
876 (map (lambda (stmt-num)
878 (vector-ref stmt-table stmt-num)))
879 (car expr-middle-stmts))))))
880 #f ; compiled-semantics
886 (+ next-middle-frag-num 1)
888 (cdr expr-middle-stmts))))))))))
893 desired-trailer-frags
897 ; Given a list of expressions, return list of locals in top level sequences.
898 ; ??? Collisions will be handled by rewriting rtl (renaming locals).
900 ; This has to be done now as the cse pass must (currently) take into account
902 ; ??? This can be done later, with an appropriate enhancement to rtx-equal?
903 ; ??? cse can be improved by ignoring local variable name (of course).
905 (define (/frag-compute-locals! expr-list)
906 (logit 2 "Computing common locals ...\n")
908 (lookup-local (lambda (local local-list)
909 (assq (car local) local-list)))
910 (local-equal? (lambda (l1 l2)
911 (and (eq? (car l1) (car l2))
912 (mode:eq? (cadr l1) (cadr l2)))))
914 (for-each (lambda (expr)
915 (let ((locals (/frag-expr-assq-locals expr)))
916 (for-each (lambda (local)
917 (let ((entry (lookup-local local result)))
919 (local-equal? local entry))
921 (set! result (cons local result)))))
928 ; Common subexpression computation.
930 ; Given a list of rtl expressions and their owners, return a pseudo-optimal
931 ; set of fragments and a usage list for each owner.
932 ; Common fragments are combined and the original expressions become a sequence
933 ; of these fragments. The result is "pseudo-optimal" in the sense that the
934 ; desired result is somewhat optimal, though no attempt is made at precise
937 ; OWNERS is a list of objects that "own" each corresponding element in EXPRS.
938 ; The owner is usually an <insn> object. Actually it'll probably always be
939 ; an <insn> object but for now I want the disassociation.
941 ; The result is a vector of six elements:
942 ; - sfrag usage table for each owner #(header middle trailer)
943 ; - statement table (vector of all statements, made with /stmt-make)
944 ; - list of sequence locals used by header sfrags
945 ; - these locals are defined at the top level so that all fragments have
947 ; - ??? Need to handle collisions among incompatible types.
952 (define (/sem-find-common-frags-1 exprs owners)
954 (if (not (elm-bound? (car owners) 'sfmt))
955 (error "sformats not computed"))
957 ; A simple procedure that calls, in order:
958 ; /frag-compute-locals!
959 ; /frag-compute-statements
961 ; The rest is shuffling of results.
963 ; Internally it's easier if OWNERS is a vector.
964 (let ((owners (list->vector owners))
965 (locals (/frag-compute-locals! exprs)))
967 ; Collect statement usage data.
968 (let ((stmt-usage (/frag-compute-statements exprs owners)))
969 (let ((stmt-usage-table (car stmt-usage))
970 (stmt-table (cdr stmt-usage)))
972 ; Compute the frags we want to create.
973 ; These are in general sequences of statements.
975 (/frag-pick-best stmt-table stmt-usage-table owners)))
977 (expr-sfrags (vector-ref desired-frags 0))
978 (headers (vector-ref desired-frags 1))
979 (trailers (vector-ref desired-frags 2))
980 (middles (vector-ref desired-frags 3))
983 (vector expr-sfrags stmt-table locals
984 headers trailers middles))))))
987 ; Cover proc of /sem-find-common-frags-1.
988 ; See its documentation.
990 (define (sem-find-common-frags insn-list)
991 (/sem-find-common-frags-1
993 (logit 2 "Simplifying/canonicalizing rtl ...\n")
995 (rtx-simplify-insn #f insn))
1000 ; Subroutine of /sfrag-create-cse-mapping to compute INSN's fragment list.
1001 ; FRAG-USAGE is a vector of 3 elements: #(header middle trailer).
1002 ; Each element is a fragment number or #f if not present.
1003 ; Numbers in FRAG-USAGE are indices relative to their respective subtables
1004 ; of FRAG-TABLE (which is a vector of all 3 tables concatenated together).
1005 ; NUM-HEADERS,NUM-TRAILERS are used to compute absolute indices.
1007 ; No header may have been created. This happens when
1008 ; it's not profitable (or possible) to merge this insn's
1009 ; leading statements with other insns. Ditto for
1010 ; trailer. However, each cti insn must have a header
1011 ; and a trailer (for pc handling setup and change).
1012 ; Try to use the middle fragment if present. Otherwise,
1013 ; use the x-header,x-trailer virtual insns.
1015 (define (/sfrag-compute-frag-list! insn frag-usage frag-table num-headers num-trailers x-header-relnum x-trailer-relnum)
1016 ; `(list #f)' is so append! works. The #f is deleted before returning.
1017 (let ((result (list #f))
1018 (header (vector-ref frag-usage 0))
1019 (middle (and (vector-ref frag-usage 1)
1020 (+ (vector-ref frag-usage 1)
1021 num-headers num-trailers)))
1022 (trailer (and (vector-ref frag-usage 2)
1023 (+ (vector-ref frag-usage 2)
1025 (x-header-num x-header-relnum)
1026 (x-trailer-num (+ x-trailer-relnum num-headers))
1029 ; cse'd header created?
1032 (append! result (list header))
1033 ; Nope. Use the middle frag if present, otherwise use x-header.
1034 ; Can't use the trailer fragment because by definition it is shared
1035 ; among several insns.
1037 ; Mark the middle frag as the header frag.
1038 (sfrag-set-header?! (vector-ref frag-table middle) #t)
1039 ; No middle, use x-header.
1040 (append! result (list x-header-num))))
1042 ; middle fragment present?
1044 (append! result (list middle)))
1046 ; cse'd trailer created?
1049 (append! result (list trailer))
1050 ; Nope. Use the middle frag if present, otherwise use x-trailer.
1051 ; Can't use the header fragment because by definition it is shared
1052 ; among several insns.
1054 ; Mark the middle frag as the trailer frag.
1055 (sfrag-set-trailer?! (vector-ref frag-table middle) #t)
1056 ; No middle, use x-trailer.
1057 (append! result (list x-trailer-num))))
1063 ; Subroutine of /sfrag-create-cse-mapping to find the fragment number of the
1064 ; x-header/x-trailer virtual frags.
1066 (define (/frag-lookup-virtual frag-list name)
1067 (let loop ((i 0) (frag-list frag-list))
1068 (if (null? frag-list)
1069 (assert (not "expected virtual insn not present"))
1070 (if (eq? name (obj:name (car frag-list)))
1072 (loop (+ i 1) (cdr frag-list)))))
1075 ; Handle complex case, find set of common header and trailer fragments.
1076 ; The result is a vector of:
1077 ; - fragment table (a vector)
1078 ; - table mapping used fragments for each insn (a list)
1081 (define (/sfrag-create-cse-mapping insn-list)
1082 (logit 1 "Creating semantic fragments for pbb engine ...\n")
1084 (let ((cse-data (sem-find-common-frags insn-list)))
1086 ; Extract the results of sem-find-common-frags.
1087 (let ((sfrag-usage-table (vector-ref cse-data 0))
1088 (stmt-table (vector-ref cse-data 1))
1089 (locals-list (vector-ref cse-data 2))
1090 (header-list1 (vector-ref cse-data 3))
1091 (trailer-list1 (vector-ref cse-data 4))
1092 (middle-list (vector-ref cse-data 5)))
1094 ; Create two special frags: x-header, x-trailer.
1095 ; These are used by insns that don't have one or the other.
1096 ; Header/trailer table indices are already computed for each insn
1097 ; so append x-header/x-trailer to the end.
1099 (append header-list1
1103 "header fragment for insns without one"
1104 (atlist-parse (make-prefix-context "semantic frag computation")
1108 (insn-sfmt (current-insn-lookup 'x-before))
1111 #f ; compiled-semantics
1117 (append trailer-list1
1121 "trailer fragment for insns without one"
1122 (atlist-parse (make-prefix-context "semantic frag computation")
1126 (insn-sfmt (current-insn-lookup 'x-before))
1129 #f ; compiled-semantics
1135 (let ((num-headers (length header-list))
1136 (num-trailers (length trailer-list))
1137 (num-middles (length middle-list)))
1139 ; Combine the three sfrag tables (headers, trailers, middles) into
1141 (let ((frag-table (list->vector (append header-list
1144 (x-header-relnum (/frag-lookup-virtual header-list 'x-header))
1145 (x-trailer-relnum (/frag-lookup-virtual trailer-list 'x-trailer))
1147 ; Convert sfrag-usage-table to one that refers to the one big
1149 (logit 2 "Computing insn frag usage ...\n")
1151 (map (lambda (insn frag-usage)
1152 (/sfrag-compute-frag-list! insn frag-usage
1154 num-headers num-trailers
1158 ; FIXME: vector->list
1159 (vector->list sfrag-usage-table)))
1161 (logit 1 "Done fragment creation.\n")
1162 (vector frag-table insn-frags locals-list)))))))
1165 ; Data analysis interface.
1167 (define /sim-sfrag-init? #f)
1168 (define (sim-sfrag-init?) /sim-sfrag-init?)
1170 ; Keep in globals for now, simplifies debugging.
1171 ; evil globals, blah blah blah.
1172 (define /sim-sfrag-insn-list #f)
1173 (define /sim-sfrag-frag-table #f)
1174 (define /sim-sfrag-usage-table #f)
1175 (define /sim-sfrag-locals-list #f)
1177 (define (sim-sfrag-insn-list)
1178 (assert /sim-sfrag-init?)
1179 /sim-sfrag-insn-list
1181 (define (sim-sfrag-frag-table)
1182 (assert /sim-sfrag-init?)
1183 /sim-sfrag-frag-table
1185 (define (sim-sfrag-usage-table)
1186 (assert /sim-sfrag-init?)
1187 /sim-sfrag-usage-table
1189 (define (sim-sfrag-locals-list)
1190 (assert /sim-sfrag-init?)
1191 /sim-sfrag-locals-list
1194 (define (sim-sfrag-init!)
1195 (set! /sim-sfrag-init? #f)
1196 (set! /sim-sfrag-insn-list #f)
1197 (set! /sim-sfrag-frag-table #f)
1198 (set! /sim-sfrag-usage-table #f)
1199 (set! /sim-sfrag-locals-list #f)
1202 (define (sim-sfrag-analyze-insns!)
1203 (if (not /sim-sfrag-init?)
1205 (set! /sim-sfrag-insn-list (non-multi-insns (non-alias-insns (current-insn-list))))
1206 (let ((frag-data (/sfrag-create-cse-mapping /sim-sfrag-insn-list)))
1207 (set! /sim-sfrag-frag-table (vector-ref frag-data 0))
1208 (set! /sim-sfrag-usage-table (vector-ref frag-data 1))
1209 (set! /sim-sfrag-locals-list (vector-ref frag-data 2)))
1210 (set! /sim-sfrag-init? #t)))
1217 (define (/frag-small-test-data)
1219 (a . (sequence VOID ((SI tmp)) (set DFLT tmp rm) (set DFLT rd rm)))
1220 (b . (sequence VOID ((SI tmp)) (set DFLT tmp rm) (set DFLT rd rm)))
1221 (c . (set DFLT rd rm))
1225 (define (/frag-test-data)
1228 (rtx-simplify-insn #f insn))
1229 (non-multi-insns (non-alias-insns (current-insn-list))))
1230 (non-multi-insns (non-alias-insns (current-insn-list))))
1233 (define test-sfrag-table #f)
1234 (define test-stmt-table #f)
1235 (define test-locals-list #f)
1236 (define test-header-list #f)
1237 (define test-trailer-list #f)
1238 (define test-middle-list #f)
1240 (define (frag-test-run)
1241 (let* ((test-data (/frag-test-data))
1242 (frag-data (sem-find-common-frags (car test-data) (cdr test-data))))
1243 (set! test-sfrag-table (vector-ref frag-data 0))
1244 (set! test-stmt-table (vector-ref frag-data 1))
1245 (set! test-locals-list (vector-ref frag-data 2))
1246 (set! test-header-list (vector-ref frag-data 3))
1247 (set! test-trailer-list (vector-ref frag-data 4))
1248 (set! test-middle-list (vector-ref frag-data 5))