1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-29 06:20:30 +02:00
guile/module/language/cps/licm.scm
Andy Wingo c54c151eb6 $primcall has a "param" member
* module/language/cps.scm ($primcall): Add "param" member, which will be
  a constant parameter to the primcall.  The idea is that constants used
  by primcalls as immediates don't need to participate in optimizations
  in any way -- they should not participate in CSE, have the same
  lifetime as the primcall so not part of DCE either, and don't need
  slot allocation.  Indirecting them through a named $const binding is
  complication for no benefit.  This change should eventually improve
  compilation time and memory usage, once we fully take advantage of it,
  as the number of labels and variables will go down.
* module/language/cps/closure-conversion.scm:
* module/language/cps/compile-bytecode.scm:
* module/language/cps/constructors.scm:
* module/language/cps/contification.scm:
* module/language/cps/cse.scm:
* module/language/cps/dce.scm:
* module/language/cps/effects-analysis.scm:
* module/language/cps/elide-values.scm:
* module/language/cps/handle-interrupts.scm:
* module/language/cps/licm.scm:
* module/language/cps/peel-loops.scm:
* module/language/cps/prune-bailouts.scm:
* module/language/cps/prune-top-level-scopes.scm:
* module/language/cps/reify-primitives.scm:
* module/language/cps/renumber.scm:
* module/language/cps/rotate-loops.scm:
* module/language/cps/self-references.scm:
* module/language/cps/simplify.scm:
* module/language/cps/slot-allocation.scm:
* module/language/cps/specialize-numbers.scm:
* module/language/cps/specialize-primcalls.scm:
* module/language/cps/split-rec.scm:
* module/language/cps/type-checks.scm:
* module/language/cps/type-fold.scm:
* module/language/cps/types.scm:
* module/language/cps/utils.scm:
* module/language/cps/verify.scm:
* module/language/tree-il/compile-cps.scm: Adapt all users.
2017-11-05 15:00:16 +01:00

308 lines
14 KiB
Scheme

;;; Continuation-passing style (CPS) intermediate language (IL)
;; Copyright (C) 2013, 2014, 2015, 2017 Free Software Foundation, Inc.
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
;;;; License as published by the Free Software Foundation; either
;;;; version 3 of the License, or (at your option) any later version.
;;;;
;;;; This library is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;;; Lesser General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Lesser General Public
;;;; License along with this library; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
;;; Commentary:
;;;
;;; Loop invariant code motion (LICM) hoists terms that don't affect a
;;; loop out of the loop, so that the loop goes faster.
;;;
;;; Code:
(define-module (language cps licm)
#:use-module (ice-9 match)
#:use-module (srfi srfi-11)
#:use-module (language cps)
#:use-module (language cps utils)
#:use-module (language cps intmap)
#:use-module (language cps intset)
#:use-module (language cps effects-analysis)
#:use-module (language cps type-checks)
#:export (hoist-loop-invariant-code))
(define (find-exits scc succs)
(intset-fold (lambda (label exits)
(if (eq? empty-intset
(intset-subtract (intmap-ref succs label) scc))
exits
(intset-add exits label)))
scc
empty-intset))
(define (find-entry scc preds)
(trivial-intset (find-exits scc preds)))
(define (list->intset l)
(persistent-intset
(fold1 (lambda (i set) (intset-add! set i)) l empty-intset)))
(define (loop-invariant? label exp loop-vars loop-effects always-reached?)
(let ((fx (intmap-ref loop-effects label)))
(and
(not (causes-effect? fx &allocation))
(or always-reached?
(not (causes-effect? fx &type-check)))
(or (not (causes-effect? fx &write))
(intmap-fold (lambda (label fx* invariant?)
(and invariant?
(not (effect-clobbers? fx fx*))))
loop-effects #t))
(or (not (causes-effect? fx &read))
(intmap-fold (lambda (label fx* invariant?)
(and invariant?
(not (effect-clobbers? fx* fx))))
loop-effects #t))
(match exp
((or ($ $const) ($ $prim) ($ $closure)) #t)
(($ $prompt) #f) ;; ?
(($ $branch) #f)
(($ $primcall 'values #f) #f)
(($ $primcall name param args)
(and-map (lambda (arg) (not (intset-ref loop-vars arg)))
args))
(($ $values args)
(and-map (lambda (arg) (not (intset-ref loop-vars arg)))
args))))))
(define (hoist-one cps label cont preds
loop-vars loop-effects pre-header-label always-reached?)
(define (filter-loop-vars names vars)
(match (vector names vars)
(#((name . names) (var . vars))
(if (intset-ref loop-vars var)
(let-values (((names vars) (filter-loop-vars names vars)))
(values (cons name names) (cons var vars)))
(filter-loop-vars names vars)))
(_ (values '() '()))))
(define (adjoin-loop-vars loop-vars vars)
(fold1 (lambda (var loop-vars) (intset-add loop-vars var))
vars loop-vars))
(define (hoist-exp src exp def-names def-vars pre-header-label)
(let* ((hoisted-label pre-header-label)
(pre-header-label (fresh-label))
(hoisted-cont
(rewrite-cont (intmap-ref cps hoisted-label)
(($ $kargs names vars)
($kargs names vars
($continue pre-header-label src ,exp)))))
(pre-header-cont
(rewrite-cont (intmap-ref cps hoisted-label)
(($ $kargs _ _ term)
($kargs def-names def-vars ,term)))))
(values (intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
pre-header-label pre-header-cont)
pre-header-label)))
(define (hoist-call src exp req rest def-names def-vars pre-header-label)
(let* ((hoisted-label pre-header-label)
(receive-label (fresh-label))
(pre-header-label (fresh-label))
(hoisted-cont
(rewrite-cont (intmap-ref cps hoisted-label)
(($ $kargs names vars)
($kargs names vars
($continue receive-label src ,exp)))))
(receive-cont
(build-cont
($kreceive req rest pre-header-label)))
(pre-header-cont
(rewrite-cont (intmap-ref cps hoisted-label)
(($ $kargs _ _ term)
($kargs def-names def-vars ,term)))))
(values (intmap-add!
(intmap-add! (intmap-replace! cps hoisted-label hoisted-cont)
receive-label receive-cont)
pre-header-label pre-header-cont)
pre-header-label)))
(match cont
(($ $kargs names vars ($ $continue k src exp))
;; If k is a loop exit, it will be nullary.
(let-values (((names vars) (filter-loop-vars names vars)))
(match (intmap-ref cps k)
(($ $kargs def-names def-vars)
(cond
((not (loop-invariant? label exp loop-vars loop-effects
always-reached?))
(let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
(loop-vars (match exp
(($ $prompt escape? tag handler)
(match (intmap-ref cps handler)
(($ $kreceive arity kargs)
(match (intmap-ref cps kargs)
(($ $kargs names vars)
(adjoin-loop-vars loop-vars vars))))))
(_ loop-vars)))
(cont (build-cont
($kargs names vars
($continue k src ,exp))))
(always-reached?
(and always-reached?
(match exp
(($ $branch) #f)
(_ (not (causes-effect? (intmap-ref loop-effects label)
&type-check)))))))
(values cps cont loop-vars loop-effects
pre-header-label always-reached?)))
((trivial-intset (intmap-ref preds k))
(let-values
(((cps pre-header-label)
(hoist-exp src exp def-names def-vars pre-header-label))
((cont) (build-cont
($kargs names vars
($continue k src ($values ()))))))
(values cps cont loop-vars (intmap-remove loop-effects label)
pre-header-label always-reached?)))
(else
(let*-values
(((def-names def-vars)
(match (intmap-ref cps k)
(($ $kargs names vars) (values names vars))))
((loop-vars) (adjoin-loop-vars loop-vars def-vars))
((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
((cps pre-header-label)
(hoist-exp src exp def-names fresh-vars pre-header-label))
((cont) (build-cont
($kargs names vars
($continue k src ($values fresh-vars))))))
(values cps cont loop-vars (intmap-remove loop-effects label)
pre-header-label always-reached?)))))
(($ $kreceive ($ $arity req () rest) kargs)
(match (intmap-ref cps kargs)
(($ $kargs def-names def-vars)
(cond
((not (loop-invariant? label exp loop-vars loop-effects
always-reached?))
(let* ((loop-vars (adjoin-loop-vars loop-vars def-vars))
(cont (build-cont
($kargs names vars
($continue k src ,exp)))))
(values cps cont loop-vars loop-effects pre-header-label #f)))
((trivial-intset (intmap-ref preds k))
(let ((loop-effects
(intmap-remove (intmap-remove loop-effects label) k)))
(let-values
(((cps pre-header-label)
(hoist-call src exp req rest def-names def-vars
pre-header-label))
((cont) (build-cont
($kargs names vars
($continue kargs src ($values ()))))))
(values cps cont loop-vars loop-effects
pre-header-label always-reached?))))
(else
(let*-values
(((loop-vars) (adjoin-loop-vars loop-vars def-vars))
((fresh-vars) (map (lambda (_) (fresh-var)) def-vars))
((cps pre-header-label)
(hoist-call src exp req rest def-names fresh-vars
pre-header-label))
((cont) (build-cont
($kargs names vars
($continue kargs src
($values fresh-vars))))))
(values cps cont loop-vars loop-effects
pre-header-label always-reached?))))))))))
(($ $kreceive ($ $arity req () rest) kargs)
(values cps cont loop-vars loop-effects pre-header-label
always-reached?))))
(define (hoist-in-loop cps entry body-labels succs preds effects)
(let* ((interior-succs (intmap-map (lambda (label succs)
(intset-intersect succs body-labels))
succs))
(sorted-labels (compute-reverse-post-order interior-succs entry))
(header-label (fresh-label))
(header-cont (intmap-ref cps entry))
(loop-vars (match header-cont
(($ $kargs names vars) (list->intset vars))))
(loop-effects (persistent-intmap
(intset-fold
(lambda (label loop-effects)
(let ((label*
(if (eqv? label entry) header-label label))
(fx (intmap-ref effects label)))
(intmap-add! loop-effects label* fx)))
body-labels empty-intmap)))
(pre-header-label entry)
(pre-header-cont (match header-cont
(($ $kargs names vars term)
(let ((vars* (map (lambda (_) (fresh-var)) vars)))
(build-cont
($kargs names vars*
($continue header-label #f
($values vars*))))))))
(cps (intmap-add! cps header-label header-cont))
(cps (intmap-replace! cps pre-header-label pre-header-cont))
(to-visit (match sorted-labels
((head . tail)
(unless (eqv? head entry) (error "what?"))
(cons header-label tail)))))
(define (rename-back-edges cont)
(define (rename label) (if (eqv? label entry) header-label label))
(rewrite-cont cont
(($ $kargs names vars ($ $continue kf src ($ $branch kt exp)))
($kargs names vars
($continue (rename kf) src ($branch (rename kt) ,exp))))
(($ $kargs names vars ($ $continue k src exp))
($kargs names vars
($continue (rename k) src ,exp)))
(($ $kreceive ($ $arity req () rest) k)
($kreceive req rest (rename k)))))
(let lp ((cps cps) (to-visit to-visit)
(loop-vars loop-vars) (loop-effects loop-effects)
(pre-header-label pre-header-label) (always-reached? #t))
(match to-visit
(() cps)
((label . to-visit)
(call-with-values
(lambda ()
(hoist-one cps label (intmap-ref cps label) preds
loop-vars loop-effects
pre-header-label always-reached?))
(lambda (cps cont
loop-vars loop-effects pre-header-label always-reached?)
(lp (intmap-replace! cps label (rename-back-edges cont)) to-visit
loop-vars loop-effects pre-header-label always-reached?))))))))
(define (hoist-in-function kfun body cps)
(let* ((succs (compute-successors cps kfun))
(preds (invert-graph succs))
(loops (intmap-fold
(lambda (id scc loops)
(cond
((trivial-intset scc) loops)
((find-entry scc preds)
=> (lambda (entry) (intmap-add! loops entry scc)))
(else loops)))
(compute-strongly-connected-components succs kfun)
empty-intmap)))
(if (eq? empty-intset loops)
cps
(let ((effects (compute-effects/elide-type-checks
(intset-fold (lambda (label body-conts)
(intmap-add! body-conts label
(intmap-ref cps label)))
body empty-intmap))))
(persistent-intmap
(intmap-fold (lambda (entry scc cps)
(hoist-in-loop cps entry scc succs preds effects))
loops cps))))))
(define (hoist-loop-invariant-code cps)
(with-fresh-name-state cps
(intmap-fold hoist-in-function
(compute-reachable-functions cps)
cps)))