From 1bb7a7fa7af9157e4b6b04dfb46c2e0ddcf9cb45 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 13 Jul 2015 11:01:43 +0200
Subject: [PATCH 001/865] Reorganizing of intset/intmap helper functions
* module/language/cps2/split-rec.scm (intmap-keys): Move to utils.
* module/language/cps2/utils.scm (trivial-intset): New function.
(intmap-keys, invert-bijection, invert-partition): New functions.
---
module/language/cps2/split-rec.scm | 4 ----
module/language/cps2/utils.scm | 30 ++++++++++++++++++++++++++++++
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/module/language/cps2/split-rec.scm b/module/language/cps2/split-rec.scm
index 763ede570..20cb516a2 100644
--- a/module/language/cps2/split-rec.scm
+++ b/module/language/cps2/split-rec.scm
@@ -105,10 +105,6 @@ references."
(persistent-intset defs)))))))
(visit-fun kfun))
-(define (intmap-keys map)
- (persistent-intset
- (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
-
(define (compute-sorted-strongly-connected-components edges)
(define nodes
(intmap-keys edges))
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
index d375925c9..e4ed47389 100644
--- a/module/language/cps2/utils.scm
+++ b/module/language/cps2/utils.scm
@@ -37,7 +37,10 @@
;; Various utilities.
fold1 fold2
+ trivial-intset
intmap-map
+ intmap-keys
+ invert-bijection invert-partition
intset->intmap
worklist-fold
fixpoint
@@ -108,12 +111,39 @@
(lambda (s0 s1)
(lp l s0 s1)))))))
+(define (trivial-intset set)
+ "Returns the sole member of @var{set}, if @var{set} has exactly one
+member, or @code{#f} otherwise."
+ (let ((first (intset-next set)))
+ (and first
+ (not (intset-next set (1+ first)))
+ first)))
+
(define (intmap-map proc map)
(persistent-intmap
(intmap-fold (lambda (k v out) (intmap-replace! out k (proc k v)))
map
map)))
+(define (intmap-keys map)
+ "Return an intset of the keys in @var{map}."
+ (persistent-intset
+ (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
+
+(define (invert-bijection map)
+ "Assuming the values of @var{map} are integers and are unique, compute
+a map in which each value maps to its key. If the values are not
+unique, an error will be signalled."
+ (intmap-fold (lambda (k v out) (intmap-add out v k)) map empty-intmap))
+
+(define (invert-partition map)
+ "Assuming the values of @var{map} are disjoint intsets, compute a map
+in which each member of each set maps to its key. If the values are not
+disjoint, an error will be signalled."
+ (intmap-fold (lambda (k v* out)
+ (intset-fold (lambda (v out) (intmap-add out v k)) v* out))
+ map empty-intmap))
+
(define (intset->intmap f set)
(persistent-intmap
(intset-fold (lambda (label preds)
From 47a4727b78c146adc24cbfbeeabc3abf022e14f8 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 13 Jul 2015 11:02:29 +0200
Subject: [PATCH 002/865] intset-union fast paths
* module/language/cps/intset.scm (intset-union): Add fast paths for
union with empty intset.
---
module/language/cps/intset.scm | 2 ++
1 file changed, 2 insertions(+)
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
index 87956c525..7f8316ea7 100644
--- a/module/language/cps/intset.scm
+++ b/module/language/cps/intset.scm
@@ -508,6 +508,8 @@
(match (cons a b)
((($ a-min a-shift a-root) . ($ b-min b-shift b-root))
(cond
+ ((not b-root) a)
+ ((not a-root) b)
((not (= b-shift a-shift))
;; Hoist the set with the lowest shift to meet the one with the
;; higher shift.
From dc27708f0b0b039d9356e8d93d71daf563906fb7 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 13 Jul 2015 11:03:00 +0200
Subject: [PATCH 003/865] Fix intset-subtract to reliably produce empty-intset
* module/language/cps/intset.scm (intset-subtract): Reliably produce
empty-intset if the result is empty.
---
module/language/cps/intset.scm | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
index 7f8316ea7..005bb7e26 100644
--- a/module/language/cps/intset.scm
+++ b/module/language/cps/intset.scm
@@ -646,10 +646,10 @@
(else (make-intset/prune a-min a-shift root)))))))))
(define (intset-subtract a b)
- (define tmp (new-leaf))
;; Intersect leaves.
(define (subtract-leaves a b)
- (logand a (lognot b)))
+ (let ((out (logand a (lognot b))))
+ (if (zero? out) #f out)))
;; Subtract B from A starting at index I; the result will be fresh.
(define (subtract-branches/fresh shift a b i fresh)
(let lp ((i 0))
@@ -721,7 +721,9 @@
(new (lp a-min a-shift old)))
(if (eq? old new)
a-root
- (clone-branch-and-set a-root a-idx new)))))))))))
+ (let ((root (clone-branch-and-set a-root a-idx new)))
+ (and (or new (not (branch-empty? root)))
+ root))))))))))))
(define (bitvector->intset bv)
(define (finish-tail out min tail)
From a15a14203e15c5c0dc037ff935c23d345070c819 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 14 Jul 2015 13:53:56 +0200
Subject: [PATCH 004/865] CPS2 renumber works with first-order CPS
* module/language/cps2/renumber.scm (compute-renaming): Add support for
$closure and $callk.
---
module/language/cps2/renumber.scm | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/module/language/cps2/renumber.scm b/module/language/cps2/renumber.scm
index 2c07e03a4..16ed29ced 100644
--- a/module/language/cps2/renumber.scm
+++ b/module/language/cps2/renumber.scm
@@ -128,6 +128,10 @@
(($ $kfun src meta self tail clause)
(rename-var self vars))
(_ vars))))
+ (define (maybe-visit-fun kfun labels vars)
+ (if (intmap-ref labels kfun (lambda (_) #f))
+ (values labels vars)
+ (visit-fun kfun labels vars)))
(define (visit-nested-funs k labels vars)
(match (intmap-ref conts k)
(($ $kargs names syms ($ $continue k src ($ $fun kfun)))
@@ -135,6 +139,14 @@
(($ $kargs names syms ($ $continue k src ($ $rec names* syms*
(($ $fun kfun) ...))))
(fold2 visit-fun kfun labels vars))
+ (($ $kargs names syms ($ $continue k src ($ $closure kfun nfree)))
+ ;; Closures with zero free vars get copy-propagated so it's
+ ;; possible to already have visited them.
+ (maybe-visit-fun kfun labels vars))
+ (($ $kargs names syms ($ $continue k src ($ $callk kfun)))
+ ;; Well-known functions never have a $closure created for them
+ ;; and are only referenced by their $callk call sites.
+ (maybe-visit-fun kfun labels vars))
(_ (values labels vars))))
(define (visit-fun kfun labels vars)
(let* ((preds (compute-predecessors conts kfun))
From 363d6498e59180e02f30ed4716ba5e4980c703db Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 14 Jul 2015 13:54:19 +0200
Subject: [PATCH 005/865] Tweak intset printing
* module/language/cps/intset.scm: Print members of set as absolute
values, not diffs from set minumum.
---
module/language/cps/intset.scm | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
index 005bb7e26..7a16464f2 100644
--- a/module/language/cps/intset.scm
+++ b/module/language/cps/intset.scm
@@ -768,13 +768,8 @@
(match ranges
(()
(format port "#<~a>" tag))
- (((0 . _) . _)
- (format port "#<~a ~a>" tag (range-string ranges)))
- (((min . end) . ranges)
- (let ((ranges (map (match-lambda
- ((start . end) (cons (- start min) (- end min))))
- (acons min end ranges))))
- (format port "#<~a ~a+~a>" tag min (range-string ranges)))))))
+ (_
+ (format port "#<~a ~a>" tag (range-string ranges))))))
(define (print-intset intset port)
(print-helper port "intset" intset))
From e0ef087cebe0f59c334cb94b0da3051684ccc9e6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 14 Jul 2015 14:12:00 +0200
Subject: [PATCH 006/865] Beginnings of first-order CPS optimization
* module/language/cps2/optimize.scm (optimize-higher-order-cps): Renamed
from "optimize".
(optimize-first-order-cps): New function.
* module/language/cps2/compile-cps.scm: Adapt.
---
module/language/cps2/compile-cps.scm | 2 +-
module/language/cps2/optimize.scm | 76 +++++++++++++++-------------
2 files changed, 42 insertions(+), 36 deletions(-)
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
index e505233ca..4294f9463 100644
--- a/module/language/cps2/compile-cps.scm
+++ b/module/language/cps2/compile-cps.scm
@@ -100,5 +100,5 @@
(convert-fun 0))
(define (compile-cps exp env opts)
- (let ((exp (renumber (optimize exp opts))))
+ (let ((exp (renumber (optimize-higher-order-cps exp opts))))
(values (conts->fun exp) env env)))
diff --git a/module/language/cps2/optimize.scm b/module/language/cps2/optimize.scm
index 3d4bb2753..4a19a57b7 100644
--- a/module/language/cps2/optimize.scm
+++ b/module/language/cps2/optimize.scm
@@ -37,7 +37,8 @@
#:use-module (language cps2 split-rec)
#:use-module (language cps2 type-fold)
#:use-module (language cps2 verify)
- #:export (optimize))
+ #:export (optimize-higher-order-cps
+ optimize-first-order-cps))
(define (kw-arg-ref args kw default)
(match (memq kw args)
@@ -51,40 +52,45 @@
(verify program)
program))
-(define* (optimize program #:optional (opts '()))
- (define (run-pass! pass kw default)
+(define-syntax-rule (define-optimizer optimize (pass kw default) ...)
+ (define* (optimize program #:optional (opts '()))
+ ;; This series of assignments to `program' used to be a series of
+ ;; let* bindings of `program', as you would imagine. In compiled
+ ;; code this is fine because the compiler is able to allocate all
+ ;; let*-bound variable to the same slot, which also means that the
+ ;; garbage collector doesn't have to retain so many copies of the
+ ;; term being optimized. However during bootstrap, the interpreter
+ ;; doesn't do this optimization, leading to excessive data retention
+ ;; as the terms are rewritten. To marginally improve bootstrap
+ ;; memory usage, here we use set! instead. The compiler should
+ ;; produce the same code in any case, though currently it does not
+ ;; because it doesn't do escape analysis on the box created for the
+ ;; set!.
+ (maybe-verify program)
(set! program
- (if (kw-arg-ref opts kw default)
- (maybe-verify (pass program))
- program)))
+ (if (kw-arg-ref opts kw default)
+ (maybe-verify (pass program))
+ program))
+ ...
+ (verify program)
+ program))
- (maybe-verify program)
+(define-optimizer optimize-higher-order-cps
+ (split-rec #:split-rec? #t)
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (prune-top-level-scopes #:prune-top-level-scopes? #t)
+ (simplify #:simplify? #t)
+ (contify #:contify? #t)
+ (inline-constructors #:inline-constructors? #t)
+ (specialize-primcalls #:specialize-primcalls? #t)
+ (elide-values #:elide-values? #t)
+ (prune-bailouts #:prune-bailouts? #t)
+ (eliminate-common-subexpressions #:cse? #t)
+ (type-fold #:type-fold? #t)
+ (resolve-self-references #:resolve-self-references? #t)
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (simplify #:simplify? #t))
- ;; This series of assignments to `program' used to be a series of let*
- ;; bindings of `program', as you would imagine. In compiled code this
- ;; is fine because the compiler is able to allocate all let*-bound
- ;; variable to the same slot, which also means that the garbage
- ;; collector doesn't have to retain so many copies of the term being
- ;; optimized. However during bootstrap, the interpreter doesn't do
- ;; this optimization, leading to excessive data retention as the terms
- ;; are rewritten. To marginally improve bootstrap memory usage, here
- ;; we use set! instead. The compiler should produce the same code in
- ;; any case, though currently it does not because it doesn't do escape
- ;; analysis on the box created for the set!.
-
- (run-pass! split-rec #:split-rec? #t)
- (run-pass! eliminate-dead-code #:eliminate-dead-code? #t)
- (run-pass! prune-top-level-scopes #:prune-top-level-scopes? #t)
- (run-pass! simplify #:simplify? #t)
- (run-pass! contify #:contify? #t)
- (run-pass! inline-constructors #:inline-constructors? #t)
- (run-pass! specialize-primcalls #:specialize-primcalls? #t)
- (run-pass! elide-values #:elide-values? #t)
- (run-pass! prune-bailouts #:prune-bailouts? #t)
- (run-pass! eliminate-common-subexpressions #:cse? #t)
- (run-pass! type-fold #:type-fold? #t)
- (run-pass! resolve-self-references #:resolve-self-references? #t)
- (run-pass! eliminate-dead-code #:eliminate-dead-code? #t)
- (run-pass! simplify #:simplify? #t)
-
- (verify program))
+(define-optimizer optimize-first-order-cps
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (simplify #:simplify? #t))
From bf5c7954ffdfe33d88ed7fe2d89ded338d82c1f9 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 14 Jul 2015 16:10:58 +0200
Subject: [PATCH 007/865] Verify pass works on first-order CPS
* module/language/cps2/verify.scm: Work with first-order CPS.
---
module/language/cps2/verify.scm | 111 +++++++++++++++++++++-----------
1 file changed, 73 insertions(+), 38 deletions(-)
diff --git a/module/language/cps2/verify.scm b/module/language/cps2/verify.scm
index 8d5504282..c833d0dfe 100644
--- a/module/language/cps2/verify.scm
+++ b/module/language/cps2/verify.scm
@@ -128,75 +128,110 @@ definitions that are available at LABEL."
(define (check-valid-var-uses conts kfun)
(define (adjoin-def var defs) (intset-add defs var))
- (let visit-fun ((kfun kfun) (free empty-intset))
- (define (visit-exp exp bound)
+ (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
+ (define (visit-exp exp bound first-order)
(define (check-use var)
(unless (intset-ref bound var)
(error "unbound var" var)))
+ (define (visit-first-order kfun)
+ (if (intset-ref first-order kfun)
+ first-order
+ (visit-fun kfun empty-intset (intset-add first-order kfun))))
(match exp
- ((or ($ $const) ($ $prim)) #t)
+ ((or ($ $const) ($ $prim)) first-order)
;; todo: $closure
(($ $fun kfun)
- (visit-fun kfun bound))
+ (visit-fun kfun bound first-order))
+ (($ $closure kfun)
+ (visit-first-order kfun))
(($ $rec names vars (($ $fun kfuns) ...))
(let ((bound (fold1 adjoin-def vars bound)))
- (for-each (lambda (kfun) (visit-fun kfun bound)) kfuns)))
+ (fold1 (lambda (kfun first-order)
+ (visit-fun kfun bound first-order))
+ kfuns first-order)))
(($ $values args)
- (for-each check-use args))
+ (for-each check-use args)
+ first-order)
(($ $call proc args)
(check-use proc)
- (for-each check-use args))
- (($ $callk k proc args)
+ (for-each check-use args)
+ first-order)
+ (($ $callk kfun proc args)
(check-use proc)
- (for-each check-use args))
+ (for-each check-use args)
+ (visit-first-order kfun))
(($ $branch kt ($ $values (arg)))
- (check-use arg))
+ (check-use arg)
+ first-order)
(($ $branch kt ($ $primcall name args))
- (for-each check-use args))
+ (for-each check-use args)
+ first-order)
(($ $primcall name args)
- (for-each check-use args))
+ (for-each check-use args)
+ first-order)
(($ $prompt escape? tag handler)
- (check-use tag))))
- (intmap-for-each
- (lambda (label bound)
+ (check-use tag)
+ first-order)))
+ (intmap-fold
+ (lambda (label bound first-order)
(let ((bound (intset-union free bound)))
(match (intmap-ref conts label)
(($ $kargs names vars ($ $continue k src exp))
- (visit-exp exp (fold1 adjoin-def vars bound)))
- (_ #t))))
- (compute-available-definitions conts kfun))))
+ (visit-exp exp (fold1 adjoin-def vars bound) first-order))
+ (_ first-order))))
+ (compute-available-definitions conts kfun)
+ first-order)))
-(define (fold-nested-funs f conts kfun seed)
- (intset-fold
- (lambda (label seed)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ ($ $fun label)))
- (f label seed))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun label) ...))))
- (fold1 f label seed))
- (_ seed)))
- (compute-function-body conts kfun)
- seed))
+(define (reachable-functions conts kfun)
+ (worklist-fold*
+ (lambda (kfun kfuns)
+ ;(pk 'verify kfun kfuns)
+ (let ((kfuns (intset-add kfuns kfun)))
+ (values (intset-fold
+ (lambda (label nested)
+ (define (return kfun*)
+ ;(pk 'return label kfuns kfun* nested)
+ (append (filter (lambda (kfun)
+ (not (intset-ref kfuns kfun)))
+ kfun*)
+ nested))
+ (define (return1 kfun) (return (list kfun)))
+ (define (return0) (return '()))
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $fun label) (return1 label))
+ (($ $rec _ _ (($ $fun labels) ...)) (return labels))
+ (($ $closure label nfree) (return1 label))
+ (($ $callk label) (return1 label))
+ (_ (return0))))
+ (_ (return0))))
+ (compute-function-body conts kfun)
+ '())
+ kfuns)))
+ (intset kfun)
+ empty-intset))
(define (check-label-partition conts kfun)
;; A continuation can only belong to one function.
- (let visit-fun ((kfun kfun) (seen empty-intmap))
- (fold-nested-funs
- visit-fun
- conts
- kfun
+ (intset-fold
+ (lambda (kfun seen)
(intset-fold
(lambda (label seen)
(intmap-add seen label kfun
(lambda (old new)
(error "label used by two functions" label old new))))
(compute-function-body conts kfun)
- seen))))
+ seen))
+ (reachable-functions conts kfun)
+ empty-intmap))
(define (compute-reachable-labels conts kfun)
- (let visit-fun ((kfun kfun) (seen empty-intset))
- (fold-nested-funs visit-fun conts kfun
- (intset-union seen (compute-function-body conts kfun)))))
+ (intset-fold
+ (lambda (kfun seen)
+ (intset-union seen (compute-function-body conts kfun)))
+ (reachable-functions conts kfun)
+ empty-intset))
(define (check-arities conts kfun)
(define (check-arity exp cont)
From 285f62a07798293b328c1989dba846a4bd1b2609 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 13 Jul 2015 11:03:53 +0200
Subject: [PATCH 008/865] Add CPS2 closure conversion module
* module/language/cps2/closure-conversion.scm: New module.
* module/Makefile.am: Add new file.
---
module/Makefile.am | 3 +-
module/language/cps2/closure-conversion.scm | 828 ++++++++++++++++++++
2 files changed, 830 insertions(+), 1 deletion(-)
create mode 100644 module/language/cps2/closure-conversion.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 88b84a1a3..270699b96 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -148,10 +148,11 @@ CPS_LANG_SOURCES = \
CPS2_LANG_SOURCES = \
language/cps2.scm \
- language/cps2/cse.scm \
+ language/cps2/closure-conversion.scm \
language/cps2/compile-cps.scm \
language/cps2/constructors.scm \
language/cps2/contification.scm \
+ language/cps2/cse.scm \
language/cps2/dce.scm \
language/cps2/effects-analysis.scm \
language/cps2/elide-values.scm \
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
new file mode 100644
index 000000000..2d20919a6
--- /dev/null
+++ b/module/language/cps2/closure-conversion.scm
@@ -0,0 +1,828 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; This pass converts a CPS term in such a way that no function has any
+;;; free variables. Instead, closures are built explicitly with
+;;; make-closure primcalls, and free variables are referenced through
+;;; the closure.
+;;;
+;;; Closure conversion also removes any $rec expressions that
+;;; contification did not handle. See (language cps) for a further
+;;; discussion of $rec.
+;;;
+;;; Code:
+
+(define-module (language cps2 closure-conversion)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (fold
+ filter-map
+ ))
+ #:use-module (srfi srfi-11)
+ #:use-module (language cps2)
+ #:use-module (language cps2 utils)
+ #:use-module (language cps2 with-cps)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (convert-closures))
+
+(define (compute-function-bodies conts kfun)
+ "Compute a map from FUN-LABEL->BODY-LABEL... for all $fun instances in
+conts."
+ (let visit-fun ((kfun kfun) (out empty-intmap))
+ (let ((body (compute-function-body conts kfun)))
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
+ (visit-fun kfun out))
+ (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
+ (fold visit-fun out kfun))
+ (_ out)))
+ body
+ (intmap-add out kfun body)))))
+
+(define (compute-program-body functions)
+ (intmap-fold (lambda (label body out) (intset-union body out))
+ functions
+ empty-intset))
+
+(define (filter-reachable conts functions)
+ (let ((reachable (compute-program-body functions)))
+ (intmap-fold
+ (lambda (label cont out)
+ (if (intset-ref reachable label)
+ out
+ (intmap-remove out label)))
+ conts conts)))
+
+(define (compute-non-operator-uses conts)
+ (persistent-intset
+ (intmap-fold
+ (lambda (label cont uses)
+ (define (add-use var uses) (intset-add! uses var))
+ (define (add-uses vars uses)
+ (match vars
+ (() uses)
+ ((var . vars) (add-uses vars (add-use var uses)))))
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) uses)
+ (($ $values args)
+ (add-uses args uses))
+ (($ $call proc args)
+ (add-uses args uses))
+ (($ $branch kt ($ $values (arg)))
+ (add-use arg uses))
+ (($ $branch kt ($ $primcall name args))
+ (add-uses args uses))
+ (($ $primcall name args)
+ (add-uses args uses))
+ (($ $prompt escape? tag handler)
+ (add-use tag uses))))
+ (_ uses)))
+ conts
+ empty-intset)))
+
+(define (compute-singly-referenced-labels conts body)
+ (define (add-ref label single multiple)
+ (define (ref k single multiple)
+ (if (intset-ref single k)
+ (values single (intset-add! multiple k))
+ (values (intset-add! single k) multiple)))
+ (define (ref0) (values single multiple))
+ (define (ref1 k) (ref k single multiple))
+ (define (ref2 k k*)
+ (if k*
+ (let-values (((single multiple) (ref k single multiple)))
+ (ref k* single multiple))
+ (ref1 k)))
+ (match (intmap-ref conts label)
+ (($ $kreceive arity k) (ref1 k))
+ (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
+ (($ $ktail) (ref0))
+ (($ $kclause arity kbody kalt) (ref2 kbody kalt))
+ (($ $kargs names syms ($ $continue k src exp))
+ (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
+ (let*-values (((single multiple) (values empty-intset empty-intset))
+ ((single multiple) (intset-fold add-ref body single multiple)))
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple))))
+
+(define (compute-function-names conts functions)
+ "Compute a map of FUN-LABEL->BOUND-VAR... for each labelled function
+whose bound vars we know."
+ (define (add-named-fun var kfun out)
+ (let ((self (match (intmap-ref conts kfun)
+ (($ $kfun src meta self) self))))
+ (intmap-add out kfun (intset var self))))
+ (intmap-fold
+ (lambda (label body out)
+ (let ((single (compute-singly-referenced-labels conts body)))
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue k _ ($ $fun kfun)))
+ (if (intset-ref single k)
+ (match (intmap-ref conts k)
+ (($ $kargs (_) (var)) (add-named-fun var kfun out))
+ (_ out))
+ out))
+ (($ $kargs _ _ ($ $continue k _ ($ $rec _ vars (($ $fun kfun) ...))))
+ (unless (intset-ref single k)
+ (error "$rec continuation has multiple predecessors??"))
+ (fold add-named-fun out vars kfun))
+ (_ out)))
+ body
+ out)))
+ functions
+ empty-intmap))
+
+(define (compute-well-known-functions conts bound->label)
+ "Compute a set of labels indicating the well-known functions in
+@var{conts}. A well-known function is a function whose bound names we
+know and which is never used in a non-operator position."
+ (intset-subtract
+ (persistent-intset
+ (intmap-fold (lambda (bound label candidates)
+ (intset-add! candidates label))
+ bound->label
+ empty-intset))
+ (persistent-intset
+ (intset-fold (lambda (var not-well-known)
+ (match (intmap-ref bound->label var (lambda (_) #f))
+ (#f not-well-known)
+ (label (intset-add! not-well-known label))))
+ (compute-non-operator-uses conts)
+ empty-intset))))
+
+(define (intset-cons i set)
+ (intset-add set i))
+
+(define (compute-shared-closures conts well-known)
+ "Compute a map LABEL->VAR indicating the sets of functions that will
+share a closure. If a functions's label is in the map, it is shared.
+The entries indicate the var of the shared closure, which will be one of
+the bound vars of the closure."
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs _ _
+ ($ $continue _ _ ($ $rec names vars (($ $fun kfuns) ...))))
+ ;; The split-rec pass should have ensured that this $rec forms a
+ ;; strongly-connected component, so the free variables from all of
+ ;; the functions will be alive as long as one of the closures is
+ ;; alive. For that reason we can consider storing all free
+ ;; variables in one closure and sharing it.
+ (let* ((kfuns-set (fold intset-cons empty-intset kfuns))
+ (unknown-kfuns (intset-subtract kfuns-set well-known)))
+ (cond
+ ((or (eq? empty-intset kfuns-set) (trivial-intset kfuns-set))
+ ;; There is only zero or one function bound here. Trivially
+ ;; shared already.
+ out)
+ ((eq? empty-intset unknown-kfuns)
+ ;; All functions are well-known; we can share a closure. Use
+ ;; the first bound variable.
+ (pk 'all-well-known kfuns)
+ (let ((closure (car vars)))
+ (intset-fold (lambda (kfun out)
+ (intmap-add out kfun closure))
+ kfuns-set out)))
+ ((trivial-intset unknown-kfuns)
+ => (lambda (unknown-kfun)
+ ;; Only one function is not-well-known. Use that
+ ;; function's closure as the shared closure.
+ (let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
+ (pk 'one-not-well-known kfuns closure)
+ (intset-fold (lambda (kfun out)
+ (intmap-add out kfun closure))
+ kfuns-set out))))
+ (else
+ ;; More than one not-well-known function means we need more
+ ;; than one proper closure, so we can't share.
+ out))))
+ (_ out)))
+ conts
+ empty-intmap))
+
+(define* (rewrite-shared-closure-calls cps functions label->bound shared kfun)
+ "Rewrite CPS such that every call to a function with a shared closure
+instead is a $callk to that label, but passing the shared closure as the
+proc argument. For recursive calls, use the appropriate 'self'
+variable, if possible. Also rewrite uses of the non-well-known but
+shared closures to use the appropriate 'self' variable, if possible."
+ ;; env := var -> (var . label)
+ (define (rewrite-fun kfun cps env)
+ (define (subst var)
+ (match (intmap-ref env var (lambda (_) #f))
+ (#f var)
+ ((var . label) var)))
+
+ (define (rename-exp label cps names vars k src exp)
+ (intmap-replace!
+ cps label
+ (build-cont
+ ($kargs names vars
+ ($continue k src
+ ,(rewrite-exp exp
+ ((or ($ $const) ($ $prim)) ,exp)
+ (($ $call proc args)
+ ,(let ((args (map subst args)))
+ (rewrite-exp (intmap-ref env proc (lambda (_) #f))
+ (#f ($call proc ,args))
+ ((closure . label) ($callk label closure ,args)))))
+ (($ $primcall name args)
+ ($primcall name ,(map subst args)))
+ (($ $branch k ($ $values (arg)))
+ ($branch k ($values ((subst arg)))))
+ (($ $branch k ($ $primcall name args))
+ ($branch k ($primcall name ,(map subst args))))
+ (($ $values args)
+ ($values ,(map subst args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (subst tag) handler))))))))
+
+ (define (visit-exp label cps names vars k src exp)
+ (define (compute-env label bound self rec-bound env)
+ (define (add-bound-var bound env)
+ (intmap-add env bound (cons self label) (lambda (old new) new)))
+ (if (intmap-ref shared label (lambda (_) #f))
+ ;; Within a function with a shared closure, rewrite
+ ;; references to bound vars to use the "self" var.
+ (fold add-bound-var env rec-bound)
+ ;; Otherwise be sure to use "self" references in any
+ ;; closure.
+ (add-bound-var bound env)))
+ (match exp
+ (($ $fun label)
+ (rewrite-fun label cps env))
+ (($ $rec names vars (($ $fun labels) ...))
+ (fold (lambda (label var cps)
+ (match (intmap-ref cps label)
+ (($ $kfun src meta self)
+ (rewrite-fun label cps
+ (compute-env label var self vars env)))))
+ cps labels vars))
+ (_ (rename-exp label cps names vars k src exp))))
+
+ (define (rewrite-cont label cps)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-exp label cps names vars k src exp))
+ (_ cps)))
+
+ (intset-fold rewrite-cont (intmap-ref functions kfun) cps))
+
+ ;; Initial environment is bound-var -> (shared-var . label) map for
+ ;; functions with shared closures.
+ (let ((env (intmap-fold (lambda (label shared env)
+ (intset-fold (lambda (bound env)
+ (intmap-add env bound
+ (cons shared label)))
+ (intset-remove
+ (intmap-ref label->bound label)
+ (match (intmap-ref cps label)
+ (($ $kfun src meta self) self)))
+ env))
+ shared
+ empty-intmap)))
+ (persistent-intmap (rewrite-fun kfun cps env))))
+
+(define (compute-free-vars conts kfun shared)
+ "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
+references."
+ (define (add-def var defs) (intset-add! defs var))
+ (define (add-defs vars defs)
+ (match vars
+ (() defs)
+ ((var . vars) (add-defs vars (add-def var defs)))))
+ (define (add-use var uses)
+ (intset-add! uses var))
+ (define (add-uses vars uses)
+ (match vars
+ (() uses)
+ ((var . vars) (add-uses vars (add-use var uses)))))
+ (define (visit-nested-funs body)
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $fun kfun)))
+ (intmap-union out (visit-fun kfun)))
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $rec _ _ (($ $fun labels) ...))))
+ (let* ((out (fold (lambda (kfun out)
+ (intmap-union out (visit-fun kfun)))
+ out labels))
+ (free (fold (lambda (kfun free)
+ (intset-union free (intmap-ref out kfun)))
+ empty-intset labels)))
+ (fold (lambda (kfun out)
+ ;; For functions that share a closure, the free
+ ;; variables for one will be the union of the free
+ ;; variables for all.
+ (if (intmap-ref shared kfun (lambda (_) #f))
+ (intmap-replace out kfun free)
+ out))
+ out
+ labels)))
+ (_ out)))
+ body
+ empty-intmap))
+ (define (visit-fun kfun)
+ (let* ((body (compute-function-body conts kfun))
+ (free (visit-nested-funs body)))
+ (call-with-values
+ (lambda ()
+ (intset-fold
+ (lambda (label defs uses)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (values
+ (add-defs vars defs)
+ (match exp
+ ((or ($ $const) ($ $prim)) uses)
+ (($ $fun kfun)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ (($ $rec names vars (($ $fun kfun) ...))
+ (fold (lambda (kfun uses)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ uses kfun))
+ (($ $values args)
+ (add-uses args uses))
+ (($ $call proc args)
+ (add-use proc (add-uses args uses)))
+ (($ $callk label proc args)
+ (add-use proc (add-uses args uses)))
+ (($ $branch kt ($ $values (arg)))
+ (add-use arg uses))
+ (($ $branch kt ($ $primcall name args))
+ (add-uses args uses))
+ (($ $primcall name args)
+ (add-uses args uses))
+ (($ $prompt escape? tag handler)
+ (add-use tag uses)))))
+ (($ $kfun src meta self)
+ (values (add-def self defs) uses))
+ (_ (values defs uses))))
+ body empty-intset empty-intset))
+ (lambda (defs uses)
+ (intmap-add free kfun (intset-subtract
+ (persistent-intset uses)
+ (persistent-intset defs)))))))
+ (visit-fun kfun))
+
+(define (eliminate-closure? label free-vars)
+ (eq? (intmap-ref free-vars label) empty-intset))
+
+(define (closure-alias label well-known free-vars)
+ (and (intset-ref well-known label)
+ (trivial-intset (intmap-ref free-vars label))))
+
+(define (prune-free-vars free-vars bound->label well-known)
+ "Given the label->bound-var map @var{free-vars}, remove free variables
+that are well-known functions with zero free variables, and replace
+references to well-known functions with one free variable with that free
+variable, until we reach a fixed point on the free-vars map."
+ (define (prune-free in-label free free-vars)
+ (intset-fold (lambda (var free)
+ (match (intmap-ref bound->label var (lambda (_) #f))
+ (#f free)
+ (label
+ (cond
+ ((eliminate-closure? label free-vars)
+ (intset-remove free var))
+ ((closure-alias label well-known free-vars)
+ => (lambda (alias)
+ ;; If VAR is free in LABEL, then ALIAS must
+ ;; also be free because its definition must
+ ;; precede VAR's definition.
+ (intset-add (intset-remove free var) alias)))
+ (else free)))))
+ free free))
+ (fixpoint (lambda (free-vars)
+ (intmap-fold (lambda (label free free-vars)
+ (intmap-replace free-vars label
+ (prune-free label free free-vars)))
+ free-vars
+ free-vars))
+ free-vars))
+
+(define (intset-find set i)
+ (let lp ((idx 0) (start #f))
+ (let ((start (intset-next set start)))
+ (cond
+ ((not start) (error "not found" set i))
+ ((= start i) idx)
+ (else (lp (1+ idx) (1+ start)))))))
+
+(define (intmap-select map set)
+ (persistent-intmap
+ (intmap-fold
+ (lambda (k v out)
+ (if (intset-ref set k)
+ (intmap-add! out k v)
+ out))
+ map
+ empty-intmap)))
+
+(define (intset-count set)
+ (intset-fold (lambda (_ count) (1+ count)) set 0))
+
+(define (convert-one cps label body free-vars bound->label well-known shared)
+ (define (well-known? label)
+ (intset-ref well-known label))
+
+ (let* ((free (intmap-ref free-vars label))
+ (nfree (intset-count free))
+ (self-known? (well-known? label))
+ (self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
+ (define (convert-arg cps var k)
+ "Convert one possibly free variable reference to a bound reference.
+
+If @var{var} is free, it is replaced by a closure reference via a
+@code{free-ref} primcall, and @var{k} is called with the new var.
+Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
+ ;; We know that var is not the name of a well-known function.
+ (cond
+ ((and=> (intmap-ref bound->label var (lambda (_) #f))
+ (lambda (kfun)
+ (and (eq? empty-intset (intmap-ref free-vars kfun))
+ kfun)))
+ ;; A not-well-known function with zero free vars. Copy as a
+ ;; constant, relying on the linker to reify just one copy.
+ => (lambda (kfun)
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ (build-term ($continue k* #f ($closure kfun 0))))))
+ ((intset-ref free var)
+ (match (vector self-known? nfree)
+ (#(#t 1)
+ ;; A reference to the one free var of a well-known function.
+ (with-cps cps
+ ($ (k self))))
+ (#(#t 2)
+ ;; A reference to one of the two free vars in a well-known
+ ;; function.
+ (let ((op (if (= var (intset-next free)) 'car 'cdr)))
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ (build-term ($continue k* #f ($primcall op (self)))))))
+ (_
+ (let* ((idx (intset-find free var))
+ (op (cond
+ ((not self-known?) 'free-ref)
+ ((<= idx #xff) 'vector-ref/immediate)
+ (else 'vector-ref))))
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ ($ (with-cps-constants ((idx idx))
+ (build-term
+ ($continue k* #f ($primcall op (self idx)))))))))))
+ (else
+ (with-cps cps
+ ($ (k var))))))
+
+ (define (convert-args cps vars k)
+ "Convert a number of possibly free references to bound references.
+@var{k} is called with the bound references, and should return the
+term."
+ (match vars
+ (()
+ (with-cps cps
+ ($ (k '()))))
+ ((var . vars)
+ (convert-arg cps var
+ (lambda (cps var)
+ (convert-args cps vars
+ (lambda (cps vars)
+ (with-cps cps
+ ($ (k (cons var vars)))))))))))
+
+ (define (allocate-closure cps k src label known? nfree)
+ "Allocate a new closure, and pass it to $var{k}."
+ (match (vector known? nfree)
+ (#(#f nfree)
+ ;; The call sites cannot be enumerated; allocate a closure.
+ (with-cps cps
+ (build-term ($continue k src ($closure label nfree)))))
+ (#(#t 2)
+ ;; Well-known closure with two free variables; the closure is a
+ ;; pair.
+ (with-cps cps
+ ($ (with-cps-constants ((false #f))
+ (build-term
+ ($continue k src ($primcall 'cons (false false))))))))
+ ;; Well-known callee with more than two free variables; the closure
+ ;; is a vector.
+ (#(#t nfree)
+ (unless (> nfree 2)
+ (error "unexpected well-known nullary, unary, or binary closure"))
+ (let ((op (if (<= nfree #xff) 'make-vector/immediate 'make-vector)))
+ (with-cps cps
+ ($ (with-cps-constants ((nfree nfree)
+ (false #f))
+ (build-term
+ ($continue k src ($primcall op (nfree false)))))))))))
+
+ (define (init-closure cps k src var known? free)
+ "Initialize the free variables @var{closure-free} in a closure
+bound to @var{var}, and continue to @var{k}."
+ (match (vector known? (intset-count free))
+ ;; Well-known callee with zero or one free variables; no
+ ;; initialization necessary.
+ (#(#t (or 0 1))
+ (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ ;; Well-known callee with two free variables; do a set-car! and
+ ;; set-cdr!.
+ (#(#t 2)
+ (let* ((free0 (intset-next free))
+ (free1 (intset-next free (1+ free0))))
+ (convert-arg cps free0
+ (lambda (cps v0)
+ (with-cps cps
+ (let$ body
+ (convert-arg free1
+ (lambda (cps v1)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($primcall 'set-cdr! (var v1))))))))
+ (letk kcdr ($kargs () () ,body))
+ (build-term
+ ($continue kcdr src ($primcall 'set-car! (var v0)))))))))
+ ;; Otherwise residualize a sequence of vector-set! or free-set!,
+ ;; depending on whether the callee is well-known or not.
+ (_
+ (let lp ((cps cps) (prev #f) (idx 0))
+ (match (intset-next free prev)
+ (#f (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ (v (with-cps cps
+ (let$ body (lp (1+ v) (1+ idx)))
+ (letk k ($kargs () () ,body))
+ ($ (convert-arg v
+ (lambda (cps v)
+ (with-cps cps
+ ($ (with-cps-constants ((idx idx))
+ (let ((op (cond
+ ((not known?) 'free-set!)
+ ((<= idx #xff) 'vector-set!/immediate)
+ (else 'vector-set!))))
+ (build-term
+ ($continue k src
+ ($primcall op (var idx v))))))))))))))))))
+
+ (define (make-single-closure cps k src kfun)
+ (let ((free (intmap-ref free-vars kfun)))
+ (match (vector (well-known? kfun) (intset-count free))
+ (#(#f 0)
+ (with-cps cps
+ (build-term ($continue k src ($closure kfun 0)))))
+ (#(#t 0)
+ (with-cps cps
+ (build-term ($continue k src ($const #f)))))
+ (#(#t 1)
+ ;; A well-known closure of one free variable is replaced
+ ;; at each use with the free variable itself, so we don't
+ ;; need a binding at all; and yet, the continuation
+ ;; expects one value, so give it something. DCE should
+ ;; clean up later.
+ (with-cps cps
+ (build-term ($continue k src ($const #f)))))
+ (#(well-known? nfree)
+ ;; A bit of a mess, but beta conversion should remove the
+ ;; final $values if possible.
+ (with-cps cps
+ (letv closure)
+ (letk k* ($kargs () () ($continue k src ($values (closure)))))
+ (let$ init (init-closure k* src closure well-known? free))
+ (letk knew ($kargs (#f) (closure) ,init))
+ ($ (allocate-closure knew src kfun well-known? nfree)))))))
+
+ ;; The callee is known, but not necessarily well-known.
+ (define (convert-known-proc-call cps k src label closure args)
+ (define (have-closure cps closure)
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($callk label closure args)))))))
+ (cond
+ ((eq? (intmap-ref free-vars label) empty-intset)
+ ;; Known call, no free variables; no closure needed.
+ ;; Pass #f as closure argument.
+ (with-cps cps
+ ($ (with-cps-constants ((false #f))
+ ($ (have-closure false))))))
+ ((and (well-known? label)
+ (trivial-intset (intmap-ref free-vars label)))
+ ;; Well-known closures with one free variable are
+ ;; replaced at their use sites by uses of the one free
+ ;; variable.
+ => (lambda (var)
+ (convert-arg cps var have-closure)))
+ (else
+ ;; Otherwise just load the proc.
+ (convert-arg cps closure have-closure))))
+
+ (define (visit-term cps term)
+ (match term
+ (($ $continue k src (or ($ $const) ($ $prim)))
+ (with-cps cps
+ term))
+
+ (($ $continue k src ($ $fun kfun))
+ (with-cps cps
+ ($ (make-single-closure k src kfun))))
+
+ ;; Remove letrec.
+ (($ $continue k src ($ $rec names vars (($ $fun kfuns) ...)))
+ (match (vector names vars kfuns)
+ (#(() () ())
+ ;; Trivial empty case.
+ (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ (#((name) (var) (kfun))
+ ;; Trivial single case. We have already proven that K has
+ ;; only LABEL as its predecessor, so we have been able
+ ;; already to rewrite free references to the bound name with
+ ;; the self name.
+ (with-cps cps
+ ($ (make-single-closure k src kfun))))
+ (#(_ _ (kfun0 . _))
+ ;; A non-trivial strongly-connected component. Does it have
+ ;; a shared closure?
+ (match (intmap-ref shared kfun0 (lambda (_) #f))
+ (#f
+ ;; Nope. Allocate closures for each function.
+ (let lp ((cps (match (intmap-ref cps k)
+ ;; Steal declarations from the continuation.
+ (($ $kargs names vals body)
+ (intmap-replace cps k
+ (build-cont
+ ($kargs () () ,body))))))
+ (in (map vector names vars kfuns))
+ (init (lambda (cps)
+ (with-cps cps
+ (build-term
+ ($continue k src ($values ())))))))
+ (match in
+ (() (init cps))
+ ((#(name var kfun) . in)
+ (let* ((known? (well-known? kfun))
+ (free (intmap-ref free-vars kfun))
+ (nfree (intset-count free)))
+ (define (next-init cps)
+ (with-cps cps
+ (let$ body (init))
+ (letk k ($kargs () () ,body))
+ ($ (init-closure k src var known? free))))
+ (with-cps cps
+ (let$ body (lp in next-init))
+ (letk k ($kargs (name) (var) ,body))
+ ($ (allocate-closure k src kfun known? nfree))))))))
+ (shared
+ ;; If shared is in the bound->var map, that means one of
+ ;; the functions is not well-known. Otherwise use kfun0
+ ;; as the function label, but just so make-single-closure
+ ;; can find the free vars, not for embedding in the
+ ;; closure.
+ (let* ((kfun (intmap-ref bound->label shared (lambda (_) kfun0)))
+ (cps (match (intmap-ref cps k)
+ ;; Make continuation declare only the shared
+ ;; closure.
+ (($ $kargs names vals body)
+ (intmap-replace cps k
+ (build-cont
+ ($kargs (#f) (shared) ,body)))))))
+ (with-cps cps
+ ($ (make-single-closure k src kfun)))))))))
+
+ (($ $continue k src ($ $call proc args))
+ (match (intmap-ref bound->label proc (lambda (_) #f))
+ (#f
+ (convert-arg cps proc
+ (lambda (cps proc)
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($call proc args)))))))))
+ (label
+ (convert-known-proc-call cps k src label proc args))))
+
+ (($ $continue k src ($ $callk label proc args))
+ (convert-known-proc-call cps k src label proc args))
+
+ (($ $continue k src ($ $primcall name args))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($primcall name args)))))))
+
+ (($ $continue k src ($ $branch kt ($ $primcall name args)))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($branch kt ($primcall name args))))))))
+
+ (($ $continue k src ($ $branch kt ($ $values (arg))))
+ (convert-arg cps arg
+ (lambda (cps arg)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($branch kt ($values (arg)))))))))
+
+ (($ $continue k src ($ $values args))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($values args)))))))
+
+ (($ $continue k src ($ $prompt escape? tag handler))
+ (convert-arg cps tag
+ (lambda (cps tag)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($prompt escape? tag handler)))))))))
+
+ (pk 'convert-one label body free self-known?)
+ (intset-fold (lambda (label cps)
+ (match (intmap-ref cps label (lambda (_) #f))
+ (($ $kargs names vars term)
+ (with-cps cps
+ (let$ term (visit-term term))
+ (setk label ($kargs names vars ,term))))
+ (_ cps)))
+ body
+ cps)))
+
+(define (convert-closures cps)
+ "Convert free reference in @var{cps} to primcalls to @code{free-ref},
+and allocate and initialize flat closures."
+ (let* ((kfun 0) ;; Ass-u-me.
+ ;; label -> body-label...
+ (functions (compute-function-bodies cps kfun))
+ (cps (filter-reachable cps functions))
+ ;; label -> bound-var...
+ (label->bound (compute-function-names cps functions))
+ ;; bound-var -> label
+ (bound->label (invert-partition label->bound))
+ ;; label...
+ (well-known (compute-well-known-functions cps bound->label))
+ ;; label -> closure-var
+ (shared (compute-shared-closures cps well-known))
+ (cps (rewrite-shared-closure-calls cps functions label->bound shared
+ kfun))
+ ;; label -> free-var...
+ (free-vars (compute-free-vars cps kfun shared))
+ (free-vars (prune-free-vars free-vars bound->label well-known)))
+ (let ((free-in-program (intmap-ref free-vars kfun)))
+ (unless (eq? empty-intset free-in-program)
+ (error "Expected no free vars in program" free-in-program)))
+ (with-fresh-name-state cps
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label body cps)
+ (convert-one cps label body free-vars bound->label well-known shared))
+ functions
+ cps)))))
+
+;;; Local Variables:
+;;; eval: (put 'convert-arg 'scheme-indent-function 2)
+;;; eval: (put 'convert-args 'scheme-indent-function 2)
+;;; End:
From 981802c4c228c9f662ebb22cefcbb241cf2b107b Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 15 Jul 2015 09:43:33 +0200
Subject: [PATCH 009/865] Wire up new closure conversion pass
* module/language/cps/compile-bytecode.scm (compile-bytecode): Only
convert closures if the #:cps2-convert? option is not passed.
* module/language/cps2/compile-cps.scm (conts->fun*, compile-cps): Add
support for CPS2 closure conversion, disabled by default.
---
module/language/cps/compile-bytecode.scm | 4 +++-
module/language/cps2/compile-cps.scm | 27 ++++++++++++++++++++----
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index 86a3db733..b66b1a693 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -517,7 +517,9 @@
;;
;; (set! exp (optimize exp opts))
- (set! exp (convert-closures exp))
+ (set! exp (if (not (kw-arg-ref opts #:cps2-convert? #f))
+ (convert-closures exp)
+ exp))
;; first-order optimization should go here
(set! exp (reify-primitives exp))
(set! exp (renumber exp))
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
index 4294f9463..da51d3536 100644
--- a/module/language/cps2/compile-cps.scm
+++ b/module/language/cps2/compile-cps.scm
@@ -27,6 +27,7 @@
#:use-module (language cps2)
#:use-module ((language cps) #:prefix cps:)
#:use-module (language cps2 utils)
+ #:use-module (language cps2 closure-conversion)
#:use-module (language cps2 optimize)
#:use-module (language cps2 renumber)
#:use-module (language cps intmap)
@@ -34,7 +35,7 @@
;; Precondition: For each function in CONTS, the continuation names are
;; topologically sorted.
-(define (conts->fun conts)
+(define* (conts->fun conts #:optional (kfun 0))
(define (convert-fun kfun)
(let ((doms (compute-dom-edges (compute-idoms conts kfun))))
(define (visit-cont label)
@@ -97,8 +98,26 @@
(($ $kfun src meta self tail clause)
(kfun (cps:$kfun src meta self (tail (cps:$ktail))
,(visit-clause clause)))))))
- (convert-fun 0))
+ (convert-fun kfun))
+
+(define (conts->fun* conts)
+ (cps:build-cps-term
+ (cps:$program
+ ,(intmap-fold-right (lambda (label cont out)
+ (match cont
+ (($ $kfun)
+ (cons (conts->fun conts label) out))
+ (_ out)))
+ conts
+ '()))))
+
+(define (kw-arg-ref args kw default)
+ (match (memq kw args)
+ ((_ val . _) val)
+ (_ default)))
(define (compile-cps exp env opts)
- (let ((exp (renumber (optimize-higher-order-cps exp opts))))
- (values (conts->fun exp) env env)))
+ (let ((exp (optimize-higher-order-cps exp opts)))
+ (if (kw-arg-ref opts #:cps2-convert? #f)
+ (values (conts->fun* (renumber (convert-closures exp))) env env)
+ (values (conts->fun (renumber exp)) env env))))
From e9e6da1902b116af36f3daa57f0caec5a04b9fa6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 15 Jul 2015 15:15:41 +0200
Subject: [PATCH 010/865] closure-conversion docstring tweak
* module/language/cps2/closure-conversion.scm (prune-free-vars): Fix
docstring.
---
module/language/cps2/closure-conversion.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
index 2d20919a6..cf15e1584 100644
--- a/module/language/cps2/closure-conversion.scm
+++ b/module/language/cps2/closure-conversion.scm
@@ -401,7 +401,7 @@ references."
(define (prune-free-vars free-vars bound->label well-known)
"Given the label->bound-var map @var{free-vars}, remove free variables
-that are well-known functions with zero free variables, and replace
+that are known functions with zero free variables, and replace
references to well-known functions with one free variable with that free
variable, until we reach a fixed point on the free-vars map."
(define (prune-free in-label free free-vars)
From 6cfb7afb61343d061ad04fb28cfd496e136dd2e8 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 15 Jul 2015 16:11:09 +0200
Subject: [PATCH 011/865] CPS2 closure conversion bugfixes
* module/language/cps2/closure-conversion.scm
(rewrite-shared-closure-calls): Fix to make shared closures call the
right label.
(closure-label): New helper.
(prune-free-vars): If a shared closure is not well-known, don't use
the alias optimization.
(convert-one): Fix for shared closures with one not-well-known
closure.
---
module/language/cps2/closure-conversion.scm | 29 ++++++++++++++-------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
index cf15e1584..0ae1bf341 100644
--- a/module/language/cps2/closure-conversion.scm
+++ b/module/language/cps2/closure-conversion.scm
@@ -261,16 +261,16 @@ shared closures to use the appropriate 'self' variable, if possible."
($prompt escape? (subst tag) handler))))))))
(define (visit-exp label cps names vars k src exp)
- (define (compute-env label bound self rec-bound env)
- (define (add-bound-var bound env)
+ (define (compute-env label bound self rec-bound rec-labels env)
+ (define (add-bound-var bound label env)
(intmap-add env bound (cons self label) (lambda (old new) new)))
(if (intmap-ref shared label (lambda (_) #f))
;; Within a function with a shared closure, rewrite
;; references to bound vars to use the "self" var.
- (fold add-bound-var env rec-bound)
+ (fold add-bound-var env rec-bound rec-labels)
;; Otherwise be sure to use "self" references in any
;; closure.
- (add-bound-var bound env)))
+ (add-bound-var bound label env)))
(match exp
(($ $fun label)
(rewrite-fun label cps env))
@@ -279,7 +279,8 @@ shared closures to use the appropriate 'self' variable, if possible."
(match (intmap-ref cps label)
(($ $kfun src meta self)
(rewrite-fun label cps
- (compute-env label var self vars env)))))
+ (compute-env label var self vars labels
+ env)))))
cps labels vars))
(_ (rename-exp label cps names vars k src exp))))
@@ -395,11 +396,18 @@ references."
(define (eliminate-closure? label free-vars)
(eq? (intmap-ref free-vars label) empty-intset))
+(define (closure-label label shared bound->label)
+ (cond
+ ((intmap-ref shared label (lambda (_) #f))
+ => (lambda (closure)
+ (intmap-ref bound->label closure)))
+ (else label)))
+
(define (closure-alias label well-known free-vars)
(and (intset-ref well-known label)
(trivial-intset (intmap-ref free-vars label))))
-(define (prune-free-vars free-vars bound->label well-known)
+(define (prune-free-vars free-vars bound->label well-known shared)
"Given the label->bound-var map @var{free-vars}, remove free variables
that are known functions with zero free variables, and replace
references to well-known functions with one free variable with that free
@@ -412,7 +420,8 @@ variable, until we reach a fixed point on the free-vars map."
(cond
((eliminate-closure? label free-vars)
(intset-remove free var))
- ((closure-alias label well-known free-vars)
+ ((closure-alias (closure-label label shared bound->label)
+ well-known free-vars)
=> (lambda (alias)
;; If VAR is free in LABEL, then ALIAS must
;; also be free because its definition must
@@ -455,7 +464,7 @@ variable, until we reach a fixed point on the free-vars map."
(let* ((free (intmap-ref free-vars label))
(nfree (intset-count free))
- (self-known? (well-known? label))
+ (self-known? (well-known? (closure-label label shared bound->label)))
(self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
(define (convert-arg cps var k)
"Convert one possibly free variable reference to a bound reference.
@@ -642,7 +651,7 @@ bound to @var{var}, and continue to @var{k}."
(with-cps cps
($ (with-cps-constants ((false #f))
($ (have-closure false))))))
- ((and (well-known? label)
+ ((and (well-known? (closure-label label shared bound->label))
(trivial-intset (intmap-ref free-vars label)))
;; Well-known closures with one free variable are
;; replaced at their use sites by uses of the one free
@@ -810,7 +819,7 @@ and allocate and initialize flat closures."
kfun))
;; label -> free-var...
(free-vars (compute-free-vars cps kfun shared))
- (free-vars (prune-free-vars free-vars bound->label well-known)))
+ (free-vars (prune-free-vars free-vars bound->label well-known shared)))
(let ((free-in-program (intmap-ref free-vars kfun)))
(unless (eq? empty-intset free-in-program)
(error "Expected no free vars in program" free-in-program)))
From 030e9b76036beeffff845619875124400f33ef5b Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 15 Jul 2015 16:44:49 +0200
Subject: [PATCH 012/865] Enable CPS2 closure conversion
* module/language/cps2/closure-conversion.scm: Remove debug printfs.
* module/language/cps2/compile-cps.scm (compile-cps):
* module/language/cps/compile-bytecode.scm (compile-bytecode): Use CPS2
closure conversion by default.
---
module/language/cps/compile-bytecode.scm | 2 +-
module/language/cps2/closure-conversion.scm | 3 ---
module/language/cps2/compile-cps.scm | 2 +-
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index b66b1a693..cc696a92f 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -517,7 +517,7 @@
;;
;; (set! exp (optimize exp opts))
- (set! exp (if (not (kw-arg-ref opts #:cps2-convert? #f))
+ (set! exp (if (not (kw-arg-ref opts #:cps2-convert? #t))
(convert-closures exp)
exp))
;; first-order optimization should go here
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
index 0ae1bf341..9e3a099ce 100644
--- a/module/language/cps2/closure-conversion.scm
+++ b/module/language/cps2/closure-conversion.scm
@@ -201,7 +201,6 @@ the bound vars of the closure."
((eq? empty-intset unknown-kfuns)
;; All functions are well-known; we can share a closure. Use
;; the first bound variable.
- (pk 'all-well-known kfuns)
(let ((closure (car vars)))
(intset-fold (lambda (kfun out)
(intmap-add out kfun closure))
@@ -211,7 +210,6 @@ the bound vars of the closure."
;; Only one function is not-well-known. Use that
;; function's closure as the shared closure.
(let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
- (pk 'one-not-well-known kfuns closure)
(intset-fold (lambda (kfun out)
(intmap-add out kfun closure))
kfuns-set out))))
@@ -789,7 +787,6 @@ bound to @var{var}, and continue to @var{k}."
($continue k src
($prompt escape? tag handler)))))))))
- (pk 'convert-one label body free self-known?)
(intset-fold (lambda (label cps)
(match (intmap-ref cps label (lambda (_) #f))
(($ $kargs names vars term)
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
index da51d3536..4c0947be5 100644
--- a/module/language/cps2/compile-cps.scm
+++ b/module/language/cps2/compile-cps.scm
@@ -118,6 +118,6 @@
(define (compile-cps exp env opts)
(let ((exp (optimize-higher-order-cps exp opts)))
- (if (kw-arg-ref opts #:cps2-convert? #f)
+ (if (kw-arg-ref opts #:cps2-convert? #t)
(values (conts->fun* (renumber (convert-closures exp))) env env)
(values (conts->fun (renumber exp)) env env))))
From 263b4099182c8d6f4e7e0f266f145c1d31f3ab33 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 15 Jul 2015 17:15:03 +0200
Subject: [PATCH 013/865] Prepare DCE pass for first-order CPS2
* module/language/cps2/dce.scm (compute-live-code): Prepare for handling
first-order CPS by tracking functions in the live label set.
---
module/language/cps2/dce.scm | 92 ++++++++++++++++++++----------------
1 file changed, 52 insertions(+), 40 deletions(-)
diff --git a/module/language/cps2/dce.scm b/module/language/cps2/dce.scm
index 28ef04f23..6fa95f735 100644
--- a/module/language/cps2/dce.scm
+++ b/module/language/cps2/dce.scm
@@ -159,38 +159,37 @@ sites."
(($ $kargs _ vars) vars)
(_ #f)))
- (define (visit-live-exp label k exp live-exps live-vars)
+ (define (visit-live-exp label k exp live-labels live-vars)
(match exp
((or ($ $const) ($ $prim))
- (values live-exps live-vars))
+ (values live-labels live-vars))
(($ $fun body)
- (visit-fun body live-exps live-vars))
+ (values (intset-add live-labels body) live-vars))
(($ $rec names vars (($ $fun kfuns) ...))
(let lp ((vars vars) (kfuns kfuns)
- (live-exps live-exps) (live-vars live-vars))
+ (live-labels live-labels) (live-vars live-vars))
(match (vector vars kfuns)
- (#(() ()) (values live-exps live-vars))
+ (#(() ()) (values live-labels live-vars))
(#((var . vars) (kfun . kfuns))
- (if (var-live? var live-vars)
- (call-with-values (lambda ()
- (visit-fun kfun live-exps live-vars))
- (lambda (live-exps live-vars)
- (lp vars kfuns live-exps live-vars)))
- (lp vars kfuns live-exps live-vars))))))
+ (lp vars kfuns
+ (if (var-live? var live-vars)
+ (intset-add live-labels kfun)
+ live-labels)
+ live-vars)))))
(($ $prompt escape? tag handler)
- (values live-exps (adjoin-var tag live-vars)))
+ (values live-labels (adjoin-var tag live-vars)))
(($ $call proc args)
- (values live-exps (adjoin-vars args (adjoin-var proc live-vars))))
+ (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
(($ $callk k proc args)
- (values live-exps (adjoin-vars args (adjoin-var proc live-vars))))
+ (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
(($ $primcall name args)
- (values live-exps (adjoin-vars args live-vars)))
+ (values live-labels (adjoin-vars args live-vars)))
(($ $branch k ($ $primcall name args))
- (values live-exps (adjoin-vars args live-vars)))
+ (values live-labels (adjoin-vars args live-vars)))
(($ $branch k ($ $values (arg)))
- (values live-exps (adjoin-var arg live-vars)))
+ (values live-labels (adjoin-var arg live-vars)))
(($ $values args)
- (values live-exps
+ (values live-labels
(match (cont-defs k)
(#f (adjoin-vars args live-vars))
(defs (fold (lambda (use def live-vars)
@@ -199,11 +198,11 @@ sites."
live-vars))
live-vars args defs)))))))
- (define (visit-exp label k exp live-exps live-vars)
+ (define (visit-exp label k exp live-labels live-vars)
(cond
- ((intset-ref live-exps label)
+ ((intset-ref live-labels label)
;; Expression live already.
- (visit-live-exp label k exp live-exps live-vars))
+ (visit-live-exp label k exp live-labels live-vars))
((let ((defs (cont-defs k))
(fx (intmap-ref effects label)))
(or
@@ -233,31 +232,44 @@ sites."
(not (intset-ref known-allocations obj))))
(_ #t)))))
;; Mark expression as live and visit.
- (visit-live-exp label k exp (intset-add live-exps label) live-vars))
+ (visit-live-exp label k exp (intset-add live-labels label) live-vars))
(else
;; Still dead.
- (values live-exps live-vars))))
+ (values live-labels live-vars))))
- (define (visit-fun label live-exps live-vars)
+ (define (visit-fun label live-labels live-vars)
;; Visit uses before definitions.
(postorder-fold-local-conts2
- (lambda (label cont live-exps live-vars)
+ (lambda (label cont live-labels live-vars)
(match cont
(($ $kargs _ _ ($ $continue k src exp))
- (visit-exp label k exp live-exps live-vars))
+ (visit-exp label k exp live-labels live-vars))
(($ $kreceive arity kargs)
- (values live-exps live-vars))
+ (values live-labels live-vars))
(($ $kclause arity kargs kalt)
- (values live-exps (adjoin-vars (cont-defs kargs) live-vars)))
+ (values live-labels (adjoin-vars (cont-defs kargs) live-vars)))
(($ $kfun src meta self)
- (values live-exps (adjoin-var self live-vars)))
+ (values live-labels (adjoin-var self live-vars)))
(($ $ktail)
- (values live-exps live-vars))))
- conts label live-exps live-vars))
+ (values live-labels live-vars))))
+ conts label live-labels live-vars))
- (fixpoint (lambda (live-exps live-vars)
- (visit-fun 0 live-exps live-vars))
- empty-intset
+ (fixpoint (lambda (live-labels live-vars)
+ (let lp ((label 0)
+ (live-labels live-labels)
+ (live-vars live-vars))
+ (match (intset-next live-labels label)
+ (#f (values live-labels live-vars))
+ (label
+ (call-with-values
+ (lambda ()
+ (match (intmap-ref conts label)
+ (($ $kfun)
+ (visit-fun label live-labels live-vars))
+ (_ (values live-labels live-vars))))
+ (lambda (live-labels live-vars)
+ (lp (1+ label) live-labels live-vars)))))))
+ (intset 0)
empty-intset)))
(define-syntax adjoin-conts
@@ -271,9 +283,9 @@ sites."
((_ cps)
cps)))
-(define (process-eliminations conts live-exps live-vars)
- (define (exp-live? label)
- (intset-ref live-exps label))
+(define (process-eliminations conts live-labels live-vars)
+ (define (label-live? label)
+ (intset-ref live-labels label))
(define (value-live? var)
(intset-ref live-vars var))
(define (make-adaptor k src defs)
@@ -288,7 +300,7 @@ sites."
(define (visit-term label term cps)
(match term
(($ $continue k src exp)
- (if (exp-live? label)
+ (if (label-live? label)
(match exp
(($ $fun body)
(values (visit-fun body cps)
@@ -370,8 +382,8 @@ sites."
;; inference.
(let ((conts (renumber conts)))
(call-with-values (lambda () (compute-live-code conts))
- (lambda (live-exps live-vars)
- (process-eliminations conts live-exps live-vars)))))
+ (lambda (live-labels live-vars)
+ (process-eliminations conts live-labels live-vars)))))
;;; Local Variables:
;;; eval: (put 'adjoin-conts 'scheme-indent-function 1)
From 1b95487501e2c55bc63b3f71931993cdb52f9ec8 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 07:22:59 +0200
Subject: [PATCH 014/865] compute-reachable-functions refactor
* module/language/cps2/utils.scm (compute-reachable-functions): New
function.
* module/language/cps2/verify.scm (check-label-partition)
(compute-reachable-labels): Use the new function.
* module/language/cps2/simplify.scm (compute-singly-referenced-vars):
Allow $closure.
(compute-eta-reductions, compute-beta-reductions): Use
compute-reachable-functions, which besides being a simplification also
allows simplification to work on first-order CPS.
---
module/language/cps2/simplify.scm | 74 +++++++++++++------------------
module/language/cps2/utils.scm | 39 ++++++++++++++++
module/language/cps2/verify.scm | 46 +++----------------
3 files changed, 77 insertions(+), 82 deletions(-)
diff --git a/module/language/cps2/simplify.scm b/module/language/cps2/simplify.scm
index 685327a40..b87b044cd 100644
--- a/module/language/cps2/simplify.scm
+++ b/module/language/cps2/simplify.scm
@@ -74,7 +74,7 @@
(match cont
(($ $kargs _ _ ($ $continue _ _ exp))
(match exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec))
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
(values single multiple))
(($ $call proc args)
(ref* (cons proc args)))
@@ -118,30 +118,24 @@
(() #t)
((var . vars)
(and (intset-ref singly-used var) (singly-used? vars)))))
- (define (visit-fun kfun nested-funs eta)
- (let ((body (compute-function-body conts kfun)))
- (define (visit-cont label nested-funs eta)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src ($ $values vars)))
- (values nested-funs
- (intset-maybe-add! eta label
- (match (intmap-ref conts k)
- (($ $kargs)
- (and (not (eqv? label k)) ; A
- (not (intset-ref eta label)) ; B
- (singly-used? vars)))
- (_ #f)))))
- (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
- (values (intset-add! nested-funs kfun) eta))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
- (values (intset-add*! nested-funs kfun) eta))
- (_
- (values nested-funs eta))))
- (intset-fold visit-cont body nested-funs eta)))
- (define (visit-funs worklist eta)
- (intset-fold visit-fun worklist empty-intset eta))
+ (define (visit-fun kfun body eta)
+ (define (visit-cont label eta)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src ($ $values vars)))
+ (intset-maybe-add! eta label
+ (match (intmap-ref conts k)
+ (($ $kargs)
+ (and (not (eqv? label k)) ; A
+ (not (intset-ref eta label)) ; B
+ (singly-used? vars)))
+ (_ #f))))
+ (_
+ eta)))
+ (intset-fold visit-cont body eta))
(persistent-intset
- (worklist-fold visit-funs (intset-add empty-intset kfun) empty-intset))))
+ (intmap-fold visit-fun
+ (compute-reachable-functions conts kfun)
+ empty-intset))))
(define (eta-reduce conts kfun)
(let ((label-set (compute-eta-reductions conts kfun)))
@@ -197,32 +191,26 @@
(persistent-intset multiple))))
(define (compute-beta-reductions conts kfun)
- (define (visit-fun kfun nested-funs beta)
- (let* ((body (compute-function-body conts kfun))
- (single (compute-singly-referenced-labels conts body)))
- (define (visit-cont label nested-funs beta)
+ (define (visit-fun kfun body beta)
+ (let ((single (compute-singly-referenced-labels conts body)))
+ (define (visit-cont label beta)
(match (intmap-ref conts label)
;; A continuation's body can be inlined in place of a $values
;; expression if the continuation is a $kargs. It should only
;; be inlined if it is used only once, and not recursively.
(($ $kargs _ _ ($ $continue k src ($ $values)))
- (values nested-funs
- (intset-maybe-add! beta label
- (and (intset-ref single k)
- (match (intmap-ref conts k)
- (($ $kargs) #t)
- (_ #f))))))
- (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
- (values (intset-add nested-funs kfun) beta))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
- (values (intset-add* nested-funs kfun) beta))
+ (intset-maybe-add! beta label
+ (and (intset-ref single k)
+ (match (intmap-ref conts k)
+ (($ $kargs) #t)
+ (_ #f)))))
(_
- (values nested-funs beta))))
- (intset-fold visit-cont body nested-funs beta)))
- (define (visit-funs worklist beta)
- (intset-fold visit-fun worklist empty-intset beta))
+ beta)))
+ (intset-fold visit-cont body beta)))
(persistent-intset
- (worklist-fold visit-funs (intset-add empty-intset kfun) empty-intset)))
+ (intmap-fold visit-fun
+ (compute-reachable-functions conts kfun)
+ empty-intset)))
(define (compute-beta-var-substitutions conts label-set)
(define (add-var-substs label var-map)
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
index e4ed47389..d96b776c9 100644
--- a/module/language/cps2/utils.scm
+++ b/module/language/cps2/utils.scm
@@ -48,6 +48,7 @@
;; Flow analysis.
compute-constant-values
compute-function-body
+ compute-reachable-functions
compute-successors
invert-graph
compute-predecessors
@@ -231,6 +232,44 @@ disjoint, an error will be signalled."
(visit-cont k labels))
(_ labels)))))))))))
+(define (compute-reachable-functions conts kfun)
+ "Compute a mapping LABEL->LABEL..., where each key is a reachable
+$kfun and each associated value is the body of the function, as an
+intset."
+ (define (intset-cons i set) (intset-add set i))
+ (define (visit-fun kfun body to-visit)
+ (intset-fold
+ (lambda (label to-visit)
+ (define (return kfun*) (fold intset-cons to-visit kfun*))
+ (define (return1 kfun) (intset-add to-visit kfun))
+ (define (return0) to-visit)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $fun label) (return1 label))
+ (($ $rec _ _ (($ $fun labels) ...)) (return labels))
+ (($ $closure label nfree) (return1 label))
+ (($ $callk label) (return1 label))
+ (_ (return0))))
+ (_ (return0))))
+ body
+ to-visit))
+ (let lp ((to-visit (intset kfun)) (visited empty-intmap))
+ (let ((to-visit (intset-subtract to-visit (intmap-keys visited))))
+ (if (eq? to-visit empty-intset)
+ visited
+ (call-with-values
+ (lambda ()
+ (intset-fold
+ (lambda (kfun to-visit visited)
+ (let ((body (compute-function-body conts kfun)))
+ (values (visit-fun kfun body to-visit)
+ (intmap-add visited kfun body))))
+ to-visit
+ empty-intset
+ visited))
+ lp)))))
+
(define (compute-successors conts kfun)
(define (visit label succs)
(let visit ((label kfun) (succs empty-intmap))
diff --git a/module/language/cps2/verify.scm b/module/language/cps2/verify.scm
index c833d0dfe..79b43f4fd 100644
--- a/module/language/cps2/verify.scm
+++ b/module/language/cps2/verify.scm
@@ -182,56 +182,24 @@ definitions that are available at LABEL."
(compute-available-definitions conts kfun)
first-order)))
-(define (reachable-functions conts kfun)
- (worklist-fold*
- (lambda (kfun kfuns)
- ;(pk 'verify kfun kfuns)
- (let ((kfuns (intset-add kfuns kfun)))
- (values (intset-fold
- (lambda (label nested)
- (define (return kfun*)
- ;(pk 'return label kfuns kfun* nested)
- (append (filter (lambda (kfun)
- (not (intset-ref kfuns kfun)))
- kfun*)
- nested))
- (define (return1 kfun) (return (list kfun)))
- (define (return0) (return '()))
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- (($ $fun label) (return1 label))
- (($ $rec _ _ (($ $fun labels) ...)) (return labels))
- (($ $closure label nfree) (return1 label))
- (($ $callk label) (return1 label))
- (_ (return0))))
- (_ (return0))))
- (compute-function-body conts kfun)
- '())
- kfuns)))
- (intset kfun)
- empty-intset))
-
(define (check-label-partition conts kfun)
;; A continuation can only belong to one function.
- (intset-fold
- (lambda (kfun seen)
+ (intmap-fold
+ (lambda (kfun body seen)
(intset-fold
(lambda (label seen)
(intmap-add seen label kfun
(lambda (old new)
(error "label used by two functions" label old new))))
- (compute-function-body conts kfun)
+ body
seen))
- (reachable-functions conts kfun)
+ (compute-reachable-functions conts kfun)
empty-intmap))
(define (compute-reachable-labels conts kfun)
- (intset-fold
- (lambda (kfun seen)
- (intset-union seen (compute-function-body conts kfun)))
- (reachable-functions conts kfun)
- empty-intset))
+ (intmap-fold (lambda (kfun body seen) (intset-union seen body))
+ (compute-reachable-functions conts kfun)
+ empty-intset))
(define (check-arities conts kfun)
(define (check-arity exp cont)
From e419e9e3dfe6a7194a68ac2990d854911cddbad6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 07:24:51 +0200
Subject: [PATCH 015/865] DCE works on first-order CPS
* module/language/cps2/dce.scm (compute-live-code): Use the live-labels
set to indicate function liveness. $closure and $callk mark their
associated functions as live.
(process-eliminations): Handle $closure.
* module/language/cps2/effects-analysis.scm (expression-effects): Handle
$closure.
---
module/language/cps2/dce.scm | 31 +++++++++++++++--------
module/language/cps2/effects-analysis.scm | 2 +-
2 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/module/language/cps2/dce.scm b/module/language/cps2/dce.scm
index 6fa95f735..e743bc4a6 100644
--- a/module/language/cps2/dce.scm
+++ b/module/language/cps2/dce.scm
@@ -165,6 +165,8 @@ sites."
(values live-labels live-vars))
(($ $fun body)
(values (intset-add live-labels body) live-vars))
+ (($ $closure body)
+ (values (intset-add live-labels body) live-vars))
(($ $rec names vars (($ $fun kfuns) ...))
(let lp ((vars vars) (kfuns kfuns)
(live-labels live-labels) (live-vars live-vars))
@@ -180,8 +182,9 @@ sites."
(values live-labels (adjoin-var tag live-vars)))
(($ $call proc args)
(values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
- (($ $callk k proc args)
- (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
+ (($ $callk kfun proc args)
+ (values (intset-add live-labels kfun)
+ (adjoin-vars args (adjoin-var proc live-vars))))
(($ $primcall name args)
(values live-labels (adjoin-vars args live-vars)))
(($ $branch k ($ $primcall name args))
@@ -303,7 +306,10 @@ sites."
(if (label-live? label)
(match exp
(($ $fun body)
- (values (visit-fun body cps)
+ (values cps
+ term))
+ (($ $closure body nfree)
+ (values cps
term))
(($ $rec names vars funs)
(match (filter-map (lambda (name var fun)
@@ -314,11 +320,7 @@ sites."
(values cps
(build-term ($continue k src ($values ())))))
(((names vars funs) ...)
- (values (fold1 (lambda (fun cps)
- (match fun
- (($ $fun kfun)
- (visit-fun kfun cps))))
- funs cps)
+ (values cps
(build-term ($continue k src
($rec names vars funs)))))))
(_
@@ -370,10 +372,17 @@ sites."
(label ($kreceive req rest adapt)))))))
(_
(adjoin-conts cps (label ,cont)))))
- (define (visit-fun kfun cps)
- (fold-local-conts visit-cont conts kfun cps))
(with-fresh-name-state conts
- (persistent-intmap (visit-fun 0 empty-intmap))))
+ (persistent-intmap
+ (intmap-fold (lambda (label cont cps)
+ (match cont
+ (($ $kfun)
+ (if (label-live? label)
+ (fold-local-conts visit-cont conts label cps)
+ cps))
+ (_ cps)))
+ conts
+ empty-intmap))))
(define (eliminate-dead-code conts)
;; We work on a renumbered program so that we can easily visit uses
diff --git a/module/language/cps2/effects-analysis.scm b/module/language/cps2/effects-analysis.scm
index a41c5f2a3..ef5d8c8e9 100644
--- a/module/language/cps2/effects-analysis.scm
+++ b/module/language/cps2/effects-analysis.scm
@@ -438,7 +438,7 @@ is or might be a read or a write to the same location as A."
(match exp
((or ($ $const) ($ $prim) ($ $values))
&no-effects)
- ((or ($ $fun) ($ $rec))
+ ((or ($ $fun) ($ $rec) ($ $closure))
(&allocate &unknown-memory-kinds))
(($ $prompt)
(&write-object &prompt))
From 099784ca9178c441eaf03a37349c463b75b57523 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 07:32:52 +0200
Subject: [PATCH 016/865] Beta reduction over first-order CPS
* module/language/cps2/simplify.scm (beta-reduce): Handle $closure.
---
module/language/cps2/simplify.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/language/cps2/simplify.scm b/module/language/cps2/simplify.scm
index b87b044cd..19d7a1799 100644
--- a/module/language/cps2/simplify.scm
+++ b/module/language/cps2/simplify.scm
@@ -238,7 +238,7 @@
(build-term
($continue k src
,(rewrite-exp exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec))
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
,exp)
(($ $call proc args)
($call (subst proc) ,(map subst args)))
From 6f6a6aee9d4b40d15aabbb39b4a53e3ef3f380d6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 07:44:30 +0200
Subject: [PATCH 017/865] Optimize first-order CPS
* module/language/cps2/optimize.scm: Move comments here from
cps/compile-bytecode.scm.
* module/language/cps/compile-bytecode.scm: Remove optimization and
closure conversion calls, since CPS2 does this for us.
* module/language/cps2/compile-cps.scm (compile-cps): Use set! to save
memory at bootstrap-time. Optimize first-order CPS, to get rid of
strangeness introduced in closure conversion.
---
module/language/cps/compile-bytecode.scm | 83 +-----------------------
module/language/cps2/compile-cps.scm | 12 ++--
module/language/cps2/optimize.scm | 10 +++
3 files changed, 20 insertions(+), 85 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index cc696a92f..c07db2621 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -27,85 +27,19 @@
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (language cps)
- #:use-module (language cps closure-conversion)
- #:use-module (language cps contification)
- #:use-module (language cps constructors)
- #:use-module (language cps cse)
- #:use-module (language cps dce)
#:use-module (language cps dfg)
- #:use-module (language cps elide-values)
#:use-module (language cps primitives)
- #:use-module (language cps prune-bailouts)
- #:use-module (language cps prune-top-level-scopes)
#:use-module (language cps reify-primitives)
#:use-module (language cps renumber)
- #:use-module (language cps self-references)
- #:use-module (language cps simplify)
#:use-module (language cps slot-allocation)
- #:use-module (language cps specialize-primcalls)
- #:use-module (language cps type-fold)
#:use-module (system vm assembler)
#:export (compile-bytecode))
-;; TODO: Local var names.
-
(define (kw-arg-ref args kw default)
(match (memq kw args)
((_ val . _) val)
(_ default)))
-(define (optimize exp opts)
- (define (run-pass! pass kw default)
- (set! exp
- (if (kw-arg-ref opts kw default)
- (pass exp)
- exp)))
-
- ;; The first DCE pass is mainly to eliminate functions that aren't
- ;; called. The last is mainly to eliminate rest parameters that
- ;; aren't used, and thus shouldn't be consed.
-
- ;; This series of assignments to `env' used to be a series of let*
- ;; bindings of `env', as you would imagine. In compiled code this is
- ;; fine because the compiler is able to allocate all let*-bound
- ;; variable to the same slot, which also means that the garbage
- ;; collector doesn't have to retain so many copies of the term being
- ;; optimized. However during bootstrap, the interpreter doesn't do
- ;; this optimization, leading to excessive data retention as the terms
- ;; are rewritten. To marginally improve bootstrap memory usage, here
- ;; we use set! instead. The compiler should produce the same code in
- ;; any case, though currently it does not because it doesn't do escape
- ;; analysis on the box created for the set!.
-
- (run-pass! eliminate-dead-code #:eliminate-dead-code? #t)
- ;; The prune-top-level-scopes pass doesn't work if CSE has run
- ;; beforehand. Since hopefully we will be able to just remove all the
- ;; old CPS stuff, let's just disable the pass for now.
- ;; (run-pass! prune-top-level-scopes #:prune-top-level-scopes? #t)
- (run-pass! simplify #:simplify? #t)
- (run-pass! contify #:contify? #t)
- (run-pass! inline-constructors #:inline-constructors? #t)
- (run-pass! specialize-primcalls #:specialize-primcalls? #t)
- (run-pass! elide-values #:elide-values? #t)
- (run-pass! prune-bailouts #:prune-bailouts? #t)
- (run-pass! eliminate-common-subexpressions #:cse? #t)
- (run-pass! type-fold #:type-fold? #t)
- (run-pass! resolve-self-references #:resolve-self-references? #t)
- (run-pass! eliminate-dead-code #:eliminate-dead-code? #t)
- (run-pass! simplify #:simplify? #t)
-
- ;; Passes that are needed:
- ;;
- ;; * Abort contification: turning abort primcalls into continuation
- ;; calls, and eliding prompts if possible.
- ;;
- ;; * Loop peeling. Unrolls the first round through a loop if the
- ;; loop has effects that CSE can work on. Requires effects
- ;; analysis. When run before CSE, loop peeling is the equivalent
- ;; of loop-invariant code motion (LICM).
-
- exp)
-
(define (compile-fun f asm)
(let* ((dfg (compute-dfg f #:global? #f))
(allocation (allocate-slots f dfg)))
@@ -509,21 +443,8 @@
(compile-entry)))))
(define (compile-bytecode exp env opts)
- ;; See comment in `optimize' about the use of set!.
-
- ;; Since CPS2's optimization pass replaces CPS and uses less memory,
- ;; we disable the optimization pass for now. We'll remove it once
- ;; we're sure.
- ;;
- ;; (set! exp (optimize exp opts))
-
- (set! exp (if (not (kw-arg-ref opts #:cps2-convert? #t))
- (convert-closures exp)
- exp))
- ;; first-order optimization should go here
- (set! exp (reify-primitives exp))
- (set! exp (renumber exp))
- (let* ((asm (make-assembler)))
+ (let* ((exp (renumber (reify-primitives exp)))
+ (asm (make-assembler)))
(match exp
(($ $program funs)
(for-each (lambda (fun) (compile-fun fun asm))
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
index 4c0947be5..85b00c95c 100644
--- a/module/language/cps2/compile-cps.scm
+++ b/module/language/cps2/compile-cps.scm
@@ -117,7 +117,11 @@
(_ default)))
(define (compile-cps exp env opts)
- (let ((exp (optimize-higher-order-cps exp opts)))
- (if (kw-arg-ref opts #:cps2-convert? #t)
- (values (conts->fun* (renumber (convert-closures exp))) env env)
- (values (conts->fun (renumber exp)) env env))))
+ ;; Use set! to save memory at bootstrap-time. (The interpreter holds
+ ;; onto all free variables locally bound in a function, so if we used
+ ;; let*, we'd hold onto earlier copies of the term.)
+ (set! exp (optimize-higher-order-cps exp opts))
+ (set! exp (convert-closures exp))
+ (set! exp (optimize-first-order-cps exp opts))
+ (set! exp (renumber exp))
+ (values (conts->fun* exp) env env))
diff --git a/module/language/cps2/optimize.scm b/module/language/cps2/optimize.scm
index 4a19a57b7..9e877b918 100644
--- a/module/language/cps2/optimize.scm
+++ b/module/language/cps2/optimize.scm
@@ -75,6 +75,16 @@
(verify program)
program))
+;; Passes that are needed:
+;;
+;; * Abort contification: turning abort primcalls into continuation
+;; calls, and eliding prompts if possible.
+;;
+;; * Loop peeling. Unrolls the first round through a loop if the
+;; loop has effects that CSE can work on. Requires effects
+;; analysis. When run before CSE, loop peeling is the equivalent
+;; of loop-invariant code motion (LICM).
+;;
(define-optimizer optimize-higher-order-cps
(split-rec #:split-rec? #t)
(eliminate-dead-code #:eliminate-dead-code? #t)
From 420423f9a09902cf5a839a0d9df4ca8d79611fea Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 07:58:36 +0200
Subject: [PATCH 018/865] Remove CPS optimization passes and closure conversion
* module/language/cps/closure-conversion.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/prune-bailouts.scm:
* module/language/cps/prune-top-level-scopes.scm:
* module/language/cps/self-references.scm:
* module/language/cps/simplify.scm:
* module/language/cps/specialize-primcalls.scm:
* module/language/cps/type-fold.scm:
* module/language/cps/types.scm: Remove these files, obsoleted by CPS2.
* module/Makefile.am: Update.
---
module/Makefile.am | 14 -
module/language/cps/closure-conversion.scm | 565 -------
module/language/cps/constructors.scm | 104 --
module/language/cps/contification.scm | 414 -----
module/language/cps/cse.scm | 545 -------
module/language/cps/dce.scm | 363 -----
module/language/cps/effects-analysis.scm | 499 ------
module/language/cps/elide-values.scm | 109 --
module/language/cps/prune-bailouts.scm | 101 --
.../language/cps/prune-top-level-scopes.scm | 114 --
module/language/cps/self-references.scm | 79 -
module/language/cps/simplify.scm | 328 ----
module/language/cps/specialize-primcalls.scm | 107 --
module/language/cps/type-fold.scm | 443 -----
module/language/cps/types.scm | 1424 -----------------
15 files changed, 5209 deletions(-)
delete mode 100644 module/language/cps/closure-conversion.scm
delete mode 100644 module/language/cps/constructors.scm
delete mode 100644 module/language/cps/contification.scm
delete mode 100644 module/language/cps/cse.scm
delete mode 100644 module/language/cps/dce.scm
delete mode 100644 module/language/cps/effects-analysis.scm
delete mode 100644 module/language/cps/elide-values.scm
delete mode 100644 module/language/cps/prune-bailouts.scm
delete mode 100644 module/language/cps/prune-top-level-scopes.scm
delete mode 100644 module/language/cps/self-references.scm
delete mode 100644 module/language/cps/simplify.scm
delete mode 100644 module/language/cps/specialize-primcalls.scm
delete mode 100644 module/language/cps/type-fold.scm
delete mode 100644 module/language/cps/types.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 270699b96..188cc7626 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -123,27 +123,13 @@ TREE_IL_LANG_SOURCES = \
CPS_LANG_SOURCES = \
language/cps.scm \
- language/cps/closure-conversion.scm \
language/cps/compile-bytecode.scm \
- language/cps/constructors.scm \
- language/cps/contification.scm \
- language/cps/cse.scm \
- language/cps/dce.scm \
language/cps/dfg.scm \
- language/cps/effects-analysis.scm \
- language/cps/elide-values.scm \
language/cps/primitives.scm \
- language/cps/prune-bailouts.scm \
- language/cps/prune-top-level-scopes.scm \
language/cps/reify-primitives.scm \
language/cps/renumber.scm \
- language/cps/self-references.scm \
language/cps/slot-allocation.scm \
- language/cps/simplify.scm \
language/cps/spec.scm \
- language/cps/specialize-primcalls.scm \
- language/cps/type-fold.scm \
- language/cps/types.scm \
language/cps/verify.scm
CPS2_LANG_SOURCES = \
diff --git a/module/language/cps/closure-conversion.scm b/module/language/cps/closure-conversion.scm
deleted file mode 100644
index 49ff30f93..000000000
--- a/module/language/cps/closure-conversion.scm
+++ /dev/null
@@ -1,565 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; This pass converts a CPS term in such a way that no function has any
-;;; free variables. Instead, closures are built explicitly with
-;;; make-closure primcalls, and free variables are referenced through
-;;; the closure.
-;;;
-;;; Closure conversion also removes any $rec expressions that
-;;; contification did not handle. See (language cps) for a further
-;;; discussion of $rec.
-;;;
-;;; Code:
-
-(define-module (language cps closure-conversion)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold
- filter-map
- lset-union lset-difference
- list-index))
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:export (convert-closures))
-
-;; free := var ...
-
-(define (analyze-closures exp dfg)
- "Compute the set of free variables for all $fun instances in
-@var{exp}."
- (let ((bound-vars (make-hash-table))
- (free-vars (make-hash-table))
- (named-funs (make-hash-table))
- (well-known-vars (make-bitvector (var-counter) #t))
- (letrec-conts (make-hash-table)))
- (define (add-named-fun! var cont)
- (hashq-set! named-funs var cont)
- (match cont
- (($ $cont label ($ $kfun src meta self))
- (unless (eq? var self)
- (hashq-set! bound-vars label var)))))
- (define (clear-well-known! var)
- (bitvector-set! well-known-vars var #f))
- (define (compute-well-known-labels)
- (let ((bv (make-bitvector (label-counter) #f)))
- (hash-for-each
- (lambda (var cont)
- (match cont
- (($ $cont label ($ $kfun src meta self))
- (unless (equal? var self)
- (bitvector-set! bv label
- (and (bitvector-ref well-known-vars var)
- (bitvector-ref well-known-vars self)))))))
- named-funs)
- bv))
- (define (union a b)
- (lset-union eq? a b))
- (define (difference a b)
- (lset-difference eq? a b))
- (define (visit-cont cont bound)
- (match cont
- (($ $cont label ($ $kargs names vars body))
- (visit-term body (append vars bound)))
- (($ $cont label ($ $kfun src meta self tail clause))
- (add-named-fun! self cont)
- (let ((free (if clause
- (visit-cont clause (list self))
- '())))
- (hashq-set! free-vars label free)
- (difference free bound)))
- (($ $cont label ($ $kclause arity body alternate))
- (let ((free (visit-cont body bound)))
- (if alternate
- (union (visit-cont alternate bound) free)
- free)))
- (($ $cont) '())))
- (define (visit-term term bound)
- (match term
- (($ $letk conts body)
- (fold (lambda (cont free)
- (union (visit-cont cont bound) free))
- (visit-term body bound)
- conts))
- (($ $continue k src ($ $fun body))
- (match (lookup-predecessors k dfg)
- ((_) (match (lookup-cont k dfg)
- (($ $kargs (name) (var))
- (add-named-fun! var body))))
- (_ #f))
- (visit-cont body bound))
- (($ $continue k src ($ $rec names vars (($ $fun cont) ...)))
- (hashq-set! letrec-conts k (lookup-cont k dfg))
- (let ((bound (append vars bound)))
- (for-each add-named-fun! vars cont)
- (fold (lambda (cont free)
- (union (visit-cont cont bound) free))
- '()
- cont)))
- (($ $continue k src exp)
- (visit-exp exp bound))))
- (define (visit-exp exp bound)
- (define (adjoin var free)
- (if (or (memq var bound) (memq var free))
- free
- (cons var free)))
- (match exp
- ((or ($ $const) ($ $prim)) '())
- (($ $call proc args)
- (for-each clear-well-known! args)
- (fold adjoin (adjoin proc '()) args))
- (($ $primcall name args)
- (for-each clear-well-known! args)
- (fold adjoin '() args))
- (($ $branch kt exp)
- (visit-exp exp bound))
- (($ $values args)
- (for-each clear-well-known! args)
- (fold adjoin '() args))
- (($ $prompt escape? tag handler)
- (clear-well-known! tag)
- (adjoin tag '()))))
-
- (let ((free (visit-cont exp '())))
- (unless (null? free)
- (error "Expected no free vars in toplevel thunk" free exp))
- (values bound-vars free-vars named-funs (compute-well-known-labels)
- letrec-conts))))
-
-(define (prune-free-vars free-vars named-funs well-known var-aliases)
- (define (well-known? label)
- (bitvector-ref well-known label))
- (let ((eliminated (make-bitvector (label-counter) #f))
- (label-aliases (make-vector (label-counter) #f)))
- (let lp ((label 0))
- (let ((label (bit-position #t well-known label)))
- (when label
- (match (hashq-ref free-vars label)
- ;; Mark all well-known closures that have no free variables
- ;; for elimination.
- (() (bitvector-set! eliminated label #t))
- ;; Replace well-known closures that have just one free
- ;; variable by references to that free variable.
- ((var)
- (vector-set! label-aliases label var))
- (_ #f))
- (lp (1+ label)))))
- ;; Iterative free variable elimination.
- (let lp ()
- (let ((recurse? #f))
- (define (adjoin elt list)
- ;; Normally you wouldn't see duplicates in a free variable
- ;; list, but with aliases that is possible.
- (if (memq elt list) list (cons elt list)))
- (define (prune-free closure-label free)
- (match free
- (() '())
- ((var . free)
- (let lp ((var var) (alias-stack '()))
- (match (hashq-ref named-funs var)
- (($ $cont label)
- (cond
- ((bitvector-ref eliminated label)
- (prune-free closure-label free))
- ((vector-ref label-aliases label)
- => (lambda (var)
- (cond
- ((memq label alias-stack)
- ;; We have found a set of mutually recursive
- ;; well-known procedures, each of which only
- ;; closes over one of the others. Mark them
- ;; all for elimination.
- (for-each (lambda (label)
- (bitvector-set! eliminated label #t)
- (set! recurse? #t))
- alias-stack)
- (prune-free closure-label free))
- (else
- (lp var (cons label alias-stack))))))
- ((eq? closure-label label)
- ;; Eliminate self-reference.
- (prune-free closure-label free))
- (else
- (adjoin var (prune-free closure-label free)))))
- (_ (adjoin var (prune-free closure-label free))))))))
- (hash-for-each-handle
- (lambda (pair)
- (match pair
- ((label . ()) #t)
- ((label . free)
- (let ((orig-nfree (length free))
- (free (prune-free label free)))
- (set-cdr! pair free)
- ;; If we managed to eliminate one or more free variables
- ;; from a well-known function, it could be that we can
- ;; eliminate or alias this function as well.
- (when (and (well-known? label)
- (< (length free) orig-nfree))
- (match free
- (()
- (bitvector-set! eliminated label #t)
- (set! recurse? #t))
- ((var)
- (vector-set! label-aliases label var)
- (set! recurse? #t))
- (_ #t)))))))
- free-vars)
- ;; Iterate to fixed point.
- (when recurse? (lp))))
- ;; Populate var-aliases from label-aliases.
- (hash-for-each (lambda (var cont)
- (match cont
- (($ $cont label)
- (let ((alias (vector-ref label-aliases label)))
- (when alias
- (vector-set! var-aliases var alias))))))
- named-funs)))
-
-(define (convert-one bound label fun free-vars named-funs well-known aliases
- letrec-conts)
- (define (well-known? label)
- (bitvector-ref well-known label))
-
- (let ((free (hashq-ref free-vars label))
- (self-known? (well-known? label))
- (self (match fun (($ $kfun _ _ self) self))))
- (define (convert-free-var var k)
- "Convert one possibly free variable reference to a bound reference.
-
-If @var{var} is free, it is replaced by a closure reference via a
-@code{free-ref} primcall, and @var{k} is called with the new var.
-Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
- (cond
- ((list-index (cut eq? <> var) free)
- => (lambda (free-idx)
- (match (cons self-known? free)
- ;; A reference to the one free var of a well-known function.
- ((#t _) (k self))
- ;; A reference to one of the two free vars in a well-known
- ;; function.
- ((#t _ _)
- (let-fresh (k*) (var*)
- (build-cps-term
- ($letk ((k* ($kargs (var*) (var*) ,(k var*))))
- ($continue k* #f
- ($primcall (match free-idx (0 'car) (1 'cdr)) (self)))))))
- (_
- (let-fresh (k* kidx) (idx var*)
- (build-cps-term
- ($letk ((kidx ($kargs ('idx) (idx)
- ($letk ((k* ($kargs (var*) (var*) ,(k var*))))
- ($continue k* #f
- ($primcall
- (cond
- ((not self-known?) 'free-ref)
- ((<= free-idx #xff) 'vector-ref/immediate)
- (else 'vector-ref))
- (self idx)))))))
- ($continue kidx #f ($const free-idx)))))))))
- ((eq? var bound) (k self))
- (else (k var))))
-
- (define (convert-free-vars vars k)
- "Convert a number of possibly free references to bound references.
-@var{k} is called with the bound references, and should return the
-term."
- (match vars
- (() (k '()))
- ((var . vars)
- (convert-free-var var
- (lambda (var)
- (convert-free-vars vars
- (lambda (vars)
- (k (cons var vars)))))))))
-
- (define (allocate-closure src name var label known? free body)
- "Allocate a new closure."
- (match (cons known? free)
- ((#f . _)
- (let-fresh (k*) ()
- (build-cps-term
- ($letk ((k* ($kargs (name) (var) ,body)))
- ($continue k* src
- ($closure label (length free)))))))
- ((#t)
- ;; Well-known closure with no free variables; elide the
- ;; binding entirely.
- body)
- ((#t _)
- ;; Well-known closure with one free variable; the free var is the
- ;; closure, and no new binding need be made.
- body)
- ((#t _ _)
- ;; Well-known closure with two free variables; the closure is a
- ;; pair.
- (let-fresh (kinit kfalse) (false)
- (build-cps-term
- ($letk ((kinit ($kargs (name) (var)
- ,body))
- (kfalse ($kargs ('false) (false)
- ($continue kinit src
- ($primcall 'cons (false false))))))
- ($continue kfalse src ($const #f))))))
- ;; Well-known callee with more than two free variables; the closure
- ;; is a vector.
- ((#t . _)
- (let ((nfree (length free)))
- (let-fresh (kinit klen kfalse) (false len-var)
- (build-cps-term
- ($letk ((kinit ($kargs (name) (var) ,body))
- (kfalse
- ($kargs ('false) (false)
- ($letk ((klen
- ($kargs ('len) (len-var)
- ($continue kinit src
- ($primcall (if (<= nfree #xff)
- 'make-vector/immediate
- 'make-vector)
- (len-var false))))))
- ($continue klen src ($const nfree))))))
- ($continue kfalse src ($const #f)))))))))
-
- (define (init-closure src var known? closure-free body)
- "Initialize the free variables @var{closure-free} in a closure
-bound to @var{var}, and continue with @var{body}."
- (match (cons known? closure-free)
- ;; Well-known callee with no free variables; no initialization
- ;; necessary.
- ((#t) body)
- ;; Well-known callee with one free variable; no initialization
- ;; necessary.
- ((#t _) body)
- ;; Well-known callee with two free variables; do a set-car! and
- ;; set-cdr!.
- ((#t v0 v1)
- (let-fresh (kcar kcdr) ()
- (convert-free-var
- v0
- (lambda (v0)
- (build-cps-term
- ($letk ((kcar ($kargs () ()
- ,(convert-free-var
- v1
- (lambda (v1)
- (build-cps-term
- ($letk ((kcdr ($kargs () () ,body)))
- ($continue kcdr src
- ($primcall 'set-cdr! (var v1))))))))))
- ($continue kcar src
- ($primcall 'set-car! (var v0)))))))))
- ;; Otherwise residualize a sequence of vector-set! or free-set!,
- ;; depending on whether the callee is well-known or not.
- (_
- (fold (lambda (free idx body)
- (let-fresh (k) (idxvar)
- (build-cps-term
- ($letk ((k ($kargs () () ,body)))
- ,(convert-free-var
- free
- (lambda (free)
- (build-cps-term
- ($letconst (('idx idxvar idx))
- ($continue k src
- ($primcall (cond
- ((not known?) 'free-set!)
- ((<= idx #xff) 'vector-set!/immediate)
- (else 'vector-set!))
- (var idxvar free)))))))))))
- body
- closure-free
- (iota (length closure-free))))))
-
- ;; Load the closure for a known call. The callee may or may not be
- ;; known at all call sites.
- (define (convert-known-proc-call var label self self-known? free k)
- ;; Well-known closures with one free variable are replaced at their
- ;; use sites by uses of the one free variable. The use sites of a
- ;; well-known closures are only in well-known proc calls, and in
- ;; free lists of other closures. Here we handle the call case; the
- ;; free list case is handled by prune-free-vars.
- (define (rename var)
- (let ((var* (vector-ref aliases var)))
- (if var*
- (rename var*)
- var)))
- (match (cons (well-known? label)
- (hashq-ref free-vars label))
- ((#t)
- ;; Calling a well-known procedure with no free variables; pass #f
- ;; as the closure.
- (let-fresh (k*) (v*)
- (build-cps-term
- ($letk ((k* ($kargs (v*) (v*) ,(k v*))))
- ($continue k* #f ($const #f))))))
- ((#t _)
- ;; Calling a well-known procedure with one free variable; pass
- ;; the free variable as the closure.
- (convert-free-var (rename var) k))
- (_
- (convert-free-var var k))))
-
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names vars body))
- (label ($kargs names vars ,(visit-term body))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail
- ,(and clause (visit-cont clause)))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont) ,cont)))
- (define (maybe-visit-cont cont)
- (match cont
- ;; We will inline the $kargs that binds letrec vars in place of
- ;; the $rec expression.
- (($ $cont label)
- (and (not (hashq-ref letrec-conts label))
- (visit-cont cont)))))
- (define (visit-term term)
- (match term
- (($ $letk conts body)
- (build-cps-term
- ($letk ,(filter-map maybe-visit-cont conts) ,(visit-term body))))
-
- (($ $continue k src (or ($ $const) ($ $prim)))
- term)
-
- (($ $continue k src ($ $fun ($ $cont kfun)))
- (let ((fun-free (hashq-ref free-vars kfun)))
- (match (cons (well-known? kfun) fun-free)
- ((known?)
- (build-cps-term
- ($continue k src ,(if known?
- (build-cps-exp ($const #f))
- (build-cps-exp ($closure kfun 0))))))
- ((#t _)
- ;; A well-known closure of one free variable is replaced
- ;; at each use with the free variable itself, so we don't
- ;; need a binding at all; and yet, the continuation
- ;; expects one value, so give it something. DCE should
- ;; clean up later.
- (build-cps-term
- ($continue k src ,(build-cps-exp ($const #f)))))
- (_
- (let-fresh () (var)
- (allocate-closure
- src #f var kfun (well-known? kfun) fun-free
- (init-closure
- src var (well-known? kfun) fun-free
- (build-cps-term ($continue k src ($values (var)))))))))))
-
- ;; Remove letrec.
- (($ $continue k src ($ $rec names vars funs))
- (let lp ((in (map list names vars funs))
- (bindings (lambda (body) body))
- (body (match (hashq-ref letrec-conts k)
- ;; Remove these letrec bindings, as we're
- ;; going to inline the body after building
- ;; each closure separately.
- (($ $kargs names syms body)
- (visit-term body)))))
- (match in
- (() (bindings body))
- (((name var ($ $fun
- (and fun-body
- ($ $cont kfun ($ $kfun src))))) . in)
- (let ((fun-free (hashq-ref free-vars kfun)))
- (lp in
- (lambda (body)
- (allocate-closure
- src name var kfun (well-known? kfun) fun-free
- (bindings body)))
- (init-closure
- src var (well-known? kfun) fun-free
- body)))))))
-
- (($ $continue k src ($ $call proc args))
- (match (hashq-ref named-funs proc)
- (($ $cont kfun)
- (convert-known-proc-call
- proc kfun self self-known? free
- (lambda (proc)
- (convert-free-vars args
- (lambda (args)
- (build-cps-term
- ($continue k src
- ($callk kfun proc args))))))))
- (#f
- (convert-free-vars (cons proc args)
- (match-lambda
- ((proc . args)
- (build-cps-term
- ($continue k src
- ($call proc args)))))))))
-
- (($ $continue k src ($ $primcall name args))
- (convert-free-vars args
- (lambda (args)
- (build-cps-term
- ($continue k src ($primcall name args))))))
-
- (($ $continue k src ($ $branch kt ($ $primcall name args)))
- (convert-free-vars args
- (lambda (args)
- (build-cps-term
- ($continue k src
- ($branch kt ($primcall name args)))))))
-
- (($ $continue k src ($ $branch kt ($ $values (arg))))
- (convert-free-var arg
- (lambda (arg)
- (build-cps-term
- ($continue k src
- ($branch kt ($values (arg))))))))
-
- (($ $continue k src ($ $values args))
- (convert-free-vars args
- (lambda (args)
- (build-cps-term
- ($continue k src ($values args))))))
-
- (($ $continue k src ($ $prompt escape? tag handler))
- (convert-free-var tag
- (lambda (tag)
- (build-cps-term
- ($continue k src
- ($prompt escape? tag handler))))))))
- (visit-cont (build-cps-cont (label ,fun)))))
-
-(define (convert-closures fun)
- "Convert free reference in @var{exp} to primcalls to @code{free-ref},
-and allocate and initialize flat closures."
- (let ((dfg (compute-dfg fun)))
- (with-fresh-name-state-from-dfg dfg
- (call-with-values (lambda () (analyze-closures fun dfg))
- (lambda (bound-vars free-vars named-funs well-known letrec-conts)
- (let ((labels (sort (hash-map->list (lambda (k v) k) free-vars) <))
- (aliases (make-vector (var-counter) #f)))
- (prune-free-vars free-vars named-funs well-known aliases)
- (build-cps-term
- ($program
- ,(map (lambda (label)
- (convert-one (hashq-ref bound-vars label) label
- (lookup-cont label dfg)
- free-vars named-funs well-known aliases
- letrec-conts))
- labels)))))))))
diff --git a/module/language/cps/constructors.scm b/module/language/cps/constructors.scm
deleted file mode 100644
index bbe779d27..000000000
--- a/module/language/cps/constructors.scm
+++ /dev/null
@@ -1,104 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Constructor inlining turns "list" primcalls into a series of conses,
-;;; and does similar transformations for "vector".
-;;;
-;;; Code:
-
-(define-module (language cps constructors)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:export (inline-constructors))
-
-(define (inline-constructors* fun)
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont sym ($ $kargs names syms body))
- (sym ($kargs names syms ,(visit-term body))))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (sym ($kfun src meta self ,tail ,(and clause (visit-cont clause)))))
- (($ $cont sym ($ $kclause arity body alternate))
- (sym ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont)
- ,cont)))
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts)
- ,(visit-term body)))
- (($ $continue k src ($ $primcall 'list args))
- ,(let-fresh (kvalues) (val)
- (build-cps-term
- ($letk ((kvalues ($kargs ('val) (val)
- ($continue k src
- ($primcall 'values (val))))))
- ,(let lp ((args args) (k kvalues))
- (match args
- (()
- (build-cps-term
- ($continue k src ($const '()))))
- ((arg . args)
- (let-fresh (ktail) (tail)
- (build-cps-term
- ($letk ((ktail ($kargs ('tail) (tail)
- ($continue k src
- ($primcall 'cons (arg tail))))))
- ,(lp args ktail)))))))))))
- (($ $continue k src ($ $primcall 'vector args))
- ,(let-fresh (kalloc) (vec len init)
- (define (initialize args n)
- (match args
- (()
- (build-cps-term
- ($continue k src ($primcall 'values (vec)))))
- ((arg . args)
- (let-fresh (knext) (idx)
- (build-cps-term
- ($letk ((knext ($kargs () ()
- ,(initialize args (1+ n)))))
- ($letconst (('idx idx n))
- ($continue knext src
- ($primcall 'vector-set! (vec idx arg))))))))))
- (build-cps-term
- ($letk ((kalloc ($kargs ('vec) (vec)
- ,(initialize args 0))))
- ($letconst (('len len (length args))
- ('init init #f))
- ($continue kalloc src
- ($primcall 'make-vector (len init))))))))
- (($ $continue k src (and fun ($ $fun)))
- ($continue k src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names syms funs))
- ($continue k src ($rec names syms (map visit-fun funs))))
- (($ $continue)
- ,term)))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(inline-constructors* body)))))
-
- (visit-cont fun))
-
-(define (inline-constructors fun)
- (with-fresh-name-state fun
- (inline-constructors* fun)))
diff --git a/module/language/cps/contification.scm b/module/language/cps/contification.scm
deleted file mode 100644
index 1f702310a..000000000
--- a/module/language/cps/contification.scm
+++ /dev/null
@@ -1,414 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Contification is a pass that turns $fun instances into $cont
-;;; instances if all calls to the $fun return to the same continuation.
-;;; This is a more rigorous variant of our old "fixpoint labels
-;;; allocation" optimization.
-;;;
-;;; See Kennedy's "Compiling with Continuations, Continued", and Fluet
-;;; and Weeks's "Contification using Dominators".
-;;;
-;;; Code:
-
-(define-module (language cps contification)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (concatenate filter-map))
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps primitives)
- #:use-module (language bytecode)
- #:export (contify))
-
-(define (compute-contification fun)
- (let* ((dfg (compute-dfg fun))
- (scope-table (make-hash-table))
- (call-substs '())
- (cont-substs '())
- (cont-splices (make-hash-table)))
- (define (subst-call! sym arities body-ks)
- (set! call-substs (acons sym (map cons arities body-ks) call-substs)))
- (define (subst-return! old-tail new-tail)
- (set! cont-substs (acons old-tail new-tail cont-substs)))
- (define (splice-conts! scope conts)
- (for-each (match-lambda
- (($ $cont k) (hashq-set! scope-table k scope)))
- conts)
- (hashq-set! cont-splices scope
- (append conts (hashq-ref cont-splices scope '()))))
-
- (define (lookup-return-cont k)
- (match (assq-ref cont-substs k)
- (#f k)
- (k (lookup-return-cont k))))
-
- ;; If K is a continuation that binds one variable, and it has only
- ;; one predecessor, return that variable.
- (define (bound-symbol k)
- (match (lookup-cont k dfg)
- (($ $kargs (_) (sym))
- (match (lookup-predecessors k dfg)
- ((_)
- ;; K has one predecessor, the one that defined SYM.
- sym)
- (_ #f)))
- (_ #f)))
-
- (define (extract-arities clause)
- (match clause
- (($ $cont _ ($ $kclause arity body alternate))
- (cons arity (extract-arities alternate)))
- (#f '())))
- (define (extract-bodies clause)
- (match clause
- (($ $cont _ ($ $kclause arity body alternate))
- (cons body (extract-bodies alternate)))
- (#f '())))
-
- (define (contify-fun term-k sym self tail arities bodies)
- (contify-funs term-k
- (list sym) (list self) (list tail)
- (list arities) (list bodies)))
-
- ;; Given a set of mutually recursive functions bound to local
- ;; variables SYMS, with self symbols SELFS, tail continuations
- ;; TAILS, arities ARITIES, and bodies BODIES, all bound in TERM-K,
- ;; contify them if we can prove that they all return to the same
- ;; continuation. Returns a true value on success, and false
- ;; otherwise.
- (define (contify-funs term-k syms selfs tails arities bodies)
- (define (unused? sym)
- (null? (lookup-uses sym dfg)))
-
- ;; Are the given args compatible with any of the arities?
- (define (applicable? proc args)
- (let lp ((arities (assq-ref (map cons syms arities) proc)))
- (match arities
- ((($ $arity req () #f () #f) . arities)
- (or (= (length args) (length req))
- (lp arities)))
- ;; If we reached the end of the arities, fail. Also fail if
- ;; the next arity in the list has optional, keyword, or rest
- ;; arguments.
- (_ #f))))
-
- ;; If the use of PROC in continuation USE is a call to PROC that
- ;; is compatible with one of the procedure's arities, return the
- ;; target continuation. Otherwise return #f.
- (define (call-target use proc)
- (match (find-call (lookup-cont use dfg))
- (($ $continue k src ($ $call proc* args))
- (and (eq? proc proc*) (not (memq proc args)) (applicable? proc args)
- ;; Converge more quickly by resolving already-contified
- ;; call targets.
- (lookup-return-cont k)))
- (_ #f)))
-
- ;; If this set of functions is always called with one
- ;; continuation, not counting tail calls between the functions,
- ;; return that continuation.
- (define (find-common-continuation)
- (let visit-syms ((syms syms) (k #f))
- (match syms
- (() k)
- ((sym . syms)
- (let visit-uses ((uses (lookup-uses sym dfg)) (k k))
- (match uses
- (() (visit-syms syms k))
- ((use . uses)
- (and=> (call-target use sym)
- (lambda (k*)
- (cond
- ((memq k* tails) (visit-uses uses k))
- ((not k) (visit-uses uses k*))
- ((eq? k k*) (visit-uses uses k))
- (else #f)))))))))))
-
- ;; Given that the functions are called with the common
- ;; continuation K, determine the scope at which to contify the
- ;; functions. If K is in scope in the term, we go ahead and
- ;; contify them there. Otherwise the scope is inside the letrec
- ;; body, and so choose the scope in which the continuation is
- ;; defined, whose free variables are a superset of the free
- ;; variables of the functions.
- ;;
- ;; There is some slight trickiness here. Call-target already uses
- ;; the information we compute within this pass. Previous
- ;; contifications may cause functions to be contified not at their
- ;; point of definition but at their point of non-recursive use.
- ;; That will cause the scope nesting to change. (It may
- ;; effectively push a function deeper down the tree -- the second
- ;; case above, a call within the letrec body.) What if we contify
- ;; to the tail of a previously contified function? We have to
- ;; track what the new scope tree will be when asking whether K
- ;; will be bound in TERM-K's scope, not the scope tree that
- ;; existed when we started the pass.
- ;;
- ;; FIXME: Does this choose the right scope for contified let-bound
- ;; functions?
- (define (find-contification-scope k)
- (define (scope-contains? scope k)
- (let ((k-scope (or (hashq-ref scope-table k)
- (let ((k-scope (lookup-block-scope k dfg)))
- (hashq-set! scope-table k k-scope)
- k-scope))))
- (or (eq? scope k-scope)
- (and k-scope (scope-contains? scope k-scope)))))
-
- ;; Find the scope of K.
- (define (continuation-scope k)
- (or (hashq-ref scope-table k)
- (let ((scope (lookup-block-scope k dfg)))
- (hashq-set! scope-table k scope)
- scope)))
-
- (let ((k-scope (continuation-scope k)))
- (if (scope-contains? k-scope term-k)
- term-k
- (match (lookup-cont k-scope dfg)
- (($ $kfun src meta self tail clause)
- ;; K is the tail of some function. If that function
- ;; has just one clause, return that clause. Otherwise
- ;; bail.
- (match clause
- (($ $cont _ ($ $kclause arity ($ $cont kargs) #f))
- kargs)
- (_ #f)))
- (_ k-scope)))))
-
- ;; We are going to contify. Mark all SYMs for replacement in
- ;; calls, and mark the tail continuations for replacement by K.
- ;; Arrange for the continuations to be spliced into SCOPE.
- (define (enqueue-contification! k scope)
- (for-each (lambda (sym tail arities bodies)
- (match bodies
- ((($ $cont body-k) ...)
- (subst-call! sym arities body-k)))
- (subst-return! tail k))
- syms tails arities bodies)
- (splice-conts! scope (concatenate bodies))
- #t)
-
- ;; "Call me maybe"
- (and (and-map unused? selfs)
- (and=> (find-common-continuation)
- (lambda (k)
- (and=> (find-contification-scope k)
- (cut enqueue-contification! k <>))))))
-
- (define (visit-fun term)
- (match term
- (($ $fun body)
- (visit-cont body))))
- (define (visit-cont cont)
- (match cont
- (($ $cont sym ($ $kargs _ _ body))
- (visit-term body sym))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (when clause (visit-cont clause)))
- (($ $cont sym ($ $kclause arity body alternate))
- (visit-cont body)
- (when alternate (visit-cont alternate)))
- (($ $cont)
- #t)))
- (define (visit-term term term-k)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body term-k))
- (($ $continue k src exp)
- (match exp
- (($ $fun
- ($ $cont fun-k
- ($ $kfun src meta self ($ $cont tail-k ($ $ktail)) clause)))
- (if (and=> (bound-symbol k)
- (lambda (sym)
- (contify-fun term-k sym self tail-k
- (extract-arities clause)
- (extract-bodies clause))))
- (begin
- (for-each visit-cont (extract-bodies clause)))
- (visit-fun exp)))
- (($ $rec names syms funs)
- (define (split-components nsf)
- ;; FIXME: Compute strongly-connected components. Currently
- ;; we just put non-recursive functions in their own
- ;; components, and lump everything else in the remaining
- ;; component.
- (define (recursive? k)
- (or-map (cut variable-free-in? <> k dfg) syms))
- (let lp ((nsf nsf) (rec '()))
- (match nsf
- (()
- (if (null? rec)
- '()
- (list rec)))
- (((and elt (n s ($ $fun ($ $cont kfun))))
- . nsf)
- (if (recursive? kfun)
- (lp nsf (cons elt rec))
- (cons (list elt) (lp nsf rec)))))))
- (define (extract-arities+bodies clauses)
- (values (map extract-arities clauses)
- (map extract-bodies clauses)))
- (define (visit-component component)
- (match component
- (((name sym fun) ...)
- (match fun
- ((($ $fun
- ($ $cont fun-k
- ($ $kfun src meta self ($ $cont tail-k ($ $ktail))
- clause)))
- ...)
- (call-with-values (lambda () (extract-arities+bodies clause))
- (lambda (arities bodies)
- ;; Technically the procedures are created in
- ;; term-k but bound for use in k. But, there is
- ;; a tight link between term-k and k, as they
- ;; are in the same block. Mark k as the
- ;; contification scope, because that's where
- ;; they'll be used. Perhaps we can fix this
- ;; with the new CPS dialect that doesn't have
- ;; $letk.
- (if (contify-funs k sym self tail-k arities bodies)
- (for-each (cut for-each visit-cont <>) bodies)
- (for-each visit-fun fun)))))))))
- (for-each visit-component
- (split-components (map list names syms funs))))
- (_ #t)))))
-
- (visit-cont fun)
- (values call-substs cont-substs cont-splices)))
-
-(define (apply-contification fun call-substs cont-substs cont-splices)
- (define (contify-call src proc args)
- (and=> (assq-ref call-substs proc)
- (lambda (clauses)
- (let lp ((clauses clauses))
- (match clauses
- (() (error "invalid contification"))
- (((($ $arity req () #f () #f) . k) . clauses)
- (if (= (length req) (length args))
- (build-cps-term
- ($continue k src
- ($values args)))
- (lp clauses)))
- ((_ . clauses) (lp clauses)))))))
- (define (continue k src exp)
- (define (lookup-return-cont k)
- (match (assq-ref cont-substs k)
- (#f k)
- (k (lookup-return-cont k))))
- (let ((k* (lookup-return-cont k)))
- ;; We are contifying this return. It must be a call or a
- ;; primcall to values, return, or return-values.
- (if (eq? k k*)
- (build-cps-term ($continue k src ,exp))
- (rewrite-cps-term exp
- (($ $primcall 'return (val))
- ($continue k* src ($primcall 'values (val))))
- (($ $values vals)
- ($continue k* src ($primcall 'values vals)))
- (_ ($continue k* src ,exp))))))
- (define (splice-continuations term-k term)
- (match (hashq-ref cont-splices term-k)
- (#f term)
- ((cont ...)
- (let lp ((term term))
- (rewrite-cps-term term
- (($ $letk conts* body)
- ($letk ,(append conts* (filter-map visit-cont cont))
- ,body))
- (body
- ($letk ,(filter-map visit-cont cont)
- ,body)))))))
- (define (visit-fun term)
- (rewrite-cps-exp term
- (($ $fun body)
- ($fun ,(visit-cont body)))))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names syms body))
- ;; Remove bindings for functions that have been contified.
- ,(rewrite-cps-cont (filter (match-lambda
- ((name sym) (not (assq sym call-substs))))
- (map list names syms))
- (((names syms) ...)
- (label ($kargs names syms ,(visit-term body label))))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail ,(and clause (visit-cont clause)))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont)
- ,cont)))
- (define (visit-term term term-k)
- (match term
- (($ $letk conts body)
- ;; Visit the body first, so we rewrite depth-first.
- (let lp ((body (visit-term body term-k)))
- ;; Because we attach contified functions on a particular
- ;; term-k, and one term-k can correspond to an arbitrarily
- ;; nested sequence of $letk instances, normalize so that all
- ;; continuations are bound by one $letk -- guaranteeing that
- ;; they are in the same scope.
- (rewrite-cps-term body
- (($ $letk conts* body)
- ($letk ,(append conts* (filter-map visit-cont conts))
- ,body))
- (body
- ($letk ,(filter-map visit-cont conts)
- ,body)))))
- (($ $continue k src exp)
- (splice-continuations
- term-k
- (match exp
- (($ $fun
- ($ $cont fun-k ($ $kfun src meta self ($ $cont tail-k))))
- ;; If the function's tail continuation has been substituted,
- ;; that means it has been contified.
- (continue k src
- (if (assq tail-k cont-substs)
- (build-cps-exp ($values ()))
- (visit-fun exp))))
- (($ $rec names syms funs)
- (match (filter (match-lambda
- ((n s f) (not (assq s call-substs))))
- (map list names syms funs))
- (() (continue k src (build-cps-exp ($values ()))))
- (((names syms funs) ...)
- (continue k src
- (build-cps-exp
- ($rec names syms (map visit-fun funs)))))))
- (($ $call proc args)
- (or (contify-call src proc args)
- (continue k src exp)))
- (_ (continue k src exp)))))))
- (visit-cont fun))
-
-(define (contify fun)
- (call-with-values (lambda () (compute-contification fun))
- (lambda (call-substs cont-substs cont-splices)
- (if (null? call-substs)
- fun
- ;; Iterate to fixed point.
- (contify
- (apply-contification fun call-substs cont-substs cont-splices))))))
diff --git a/module/language/cps/cse.scm b/module/language/cps/cse.scm
deleted file mode 100644
index c8a57ca0b..000000000
--- a/module/language/cps/cse.scm
+++ /dev/null
@@ -1,545 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Common subexpression elimination for CPS.
-;;;
-;;; Code:
-
-(define-module (language cps cse)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps effects-analysis)
- #:use-module (language cps renumber)
- #:use-module (language cps intset)
- #:use-module (rnrs bytevectors)
- #:export (eliminate-common-subexpressions))
-
-(define (cont-successors cont)
- (match cont
- (($ $kargs names syms body)
- (let lp ((body body))
- (match body
- (($ $letk conts body) (lp body))
- (($ $continue k src exp)
- (match exp
- (($ $prompt escape? tag handler) (list k handler))
- (($ $branch kt) (list k kt))
- (_ (list k)))))))
-
- (($ $kreceive arity k) (list k))
-
- (($ $kclause arity ($ $cont kbody)) (list kbody))
-
- (($ $kfun src meta self tail clause)
- (let lp ((clause clause))
- (match clause
- (($ $cont kclause ($ $kclause _ _ alt))
- (cons kclause (lp alt)))
- (#f '()))))
-
- (($ $kfun src meta self tail #f) '())
-
- (($ $ktail) '())))
-
-(define (compute-available-expressions dfg min-label label-count idoms)
- "Compute and return the continuations that may be reached if flow
-reaches a continuation N. Returns a vector of intsets, whose first
-index corresponds to MIN-LABEL, and so on."
- (let* ((effects (compute-effects dfg min-label label-count))
- ;; Vector of intsets, indicating that at a continuation N, the
- ;; values from continuations M... are available.
- (avail (make-vector label-count #f))
- (revisit-label #f))
-
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (define (get-effects label) (vector-ref effects (label->idx label)))
-
- (define (propagate! pred succ out)
- (let* ((succ-idx (label->idx succ))
- (in (match (lookup-predecessors succ dfg)
- ;; Fast path: normal control flow.
- ((_) out)
- ;; Slow path: control-flow join.
- (_ (cond
- ((vector-ref avail succ-idx)
- => (lambda (in)
- (intset-intersect in out)))
- (else out))))))
- (when (and (<= succ pred)
- (or (not revisit-label) (< succ revisit-label))
- (not (eq? in (vector-ref avail succ-idx))))
- ;; Arrange to revisit if this is not a forward edge and the
- ;; available set changed.
- (set! revisit-label succ))
- (vector-set! avail succ-idx in)))
-
- (define (clobber label in)
- (let ((fx (get-effects label)))
- (cond
- ((not (causes-effect? fx &write))
- ;; Fast-path if this expression clobbers nothing.
- in)
- (else
- ;; Kill clobbered expressions. There is no need to check on
- ;; any label before than the last dominating label that
- ;; clobbered everything.
- (let ((first (let lp ((dom label))
- (let* ((dom (vector-ref idoms (label->idx dom))))
- (and (< min-label dom)
- (let ((fx (vector-ref effects (label->idx dom))))
- (if (causes-all-effects? fx)
- dom
- (lp dom))))))))
- (let lp ((i first) (in in))
- (cond
- ((intset-next in i)
- => (lambda (i)
- (if (effect-clobbers? fx (vector-ref effects (label->idx i)))
- (lp (1+ i) (intset-remove in i))
- (lp (1+ i) in))))
- (else in))))))))
-
- (synthesize-definition-effects! effects dfg min-label label-count)
-
- (vector-set! avail 0 empty-intset)
-
- (let lp ((n 0))
- (cond
- ((< n label-count)
- (let* ((label (idx->label n))
- ;; It's possible for "in" to be #f if it has no
- ;; predecessors, as is the case for the ktail of a
- ;; function with an iloop.
- (in (or (vector-ref avail n) empty-intset))
- (out (intset-add (clobber label in) label)))
- (lookup-predecessors label dfg)
- (let visit-succs ((succs (cont-successors (lookup-cont label dfg))))
- (match succs
- (() (lp (1+ n)))
- ((succ . succs)
- (propagate! label succ out)
- (visit-succs succs))))))
- (revisit-label
- (let ((n (label->idx revisit-label)))
- (set! revisit-label #f)
- (lp n)))
- (else
- (values avail effects))))))
-
-(define (compute-truthy-expressions dfg min-label label-count)
- "Compute a \"truth map\", indicating which expressions can be shown to
-be true and/or false at each of LABEL-COUNT expressions in DFG, starting
-from MIN-LABEL. Returns a vector of intsets, each intset twice as long
-as LABEL-COUNT. The even elements of the intset indicate labels that
-may be true, and the odd ones indicate those that may be false. It
-could be that both true and false proofs are available."
- (let ((boolv (make-vector label-count #f))
- (revisit-label #f))
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (define (true-idx idx) (ash idx 1))
- (define (false-idx idx) (1+ (ash idx 1)))
-
- (define (propagate! pred succ out)
- (let* ((succ-idx (label->idx succ))
- (in (match (lookup-predecessors succ dfg)
- ;; Fast path: normal control flow.
- ((_) out)
- ;; Slow path: control-flow join.
- (_ (cond
- ((vector-ref boolv succ-idx)
- => (lambda (in)
- (intset-intersect in out)))
- (else out))))))
- (when (and (<= succ pred)
- (or (not revisit-label) (< succ revisit-label))
- (not (eq? in (vector-ref boolv succ-idx))))
- (set! revisit-label succ))
- (vector-set! boolv succ-idx in)))
-
- (vector-set! boolv 0 empty-intset)
-
- (let lp ((n 0))
- (cond
- ((< n label-count)
- (let* ((label (idx->label n))
- ;; It's possible for "in" to be #f if it has no
- ;; predecessors, as is the case for the ktail of a
- ;; function with an iloop.
- (in (or (vector-ref boolv n) empty-intset)))
- (define (default-propagate)
- (let visit-succs ((succs (cont-successors (lookup-cont label dfg))))
- (match succs
- (() (lp (1+ n)))
- ((succ . succs)
- (propagate! label succ in)
- (visit-succs succs)))))
- (match (lookup-cont label dfg)
- (($ $kargs names syms body)
- (match (find-call body)
- (($ $continue k src ($ $branch kt))
- (propagate! label k (intset-add in (false-idx n)))
- (propagate! label kt (intset-add in (true-idx n)))
- (lp (1+ n)))
- (_ (default-propagate))))
- (_ (default-propagate)))))
- (revisit-label
- (let ((n (label->idx revisit-label)))
- (set! revisit-label #f)
- (lp n)))
- (else boolv)))))
-
-;; Returns a map of label-idx -> (var-idx ...) indicating the variables
-;; defined by a given labelled expression.
-(define (compute-defs dfg min-label label-count)
- (define (cont-defs k)
- (match (lookup-cont k dfg)
- (($ $kargs names vars) vars)
- (_ '())))
- (define (idx->label idx) (+ idx min-label))
- (let ((defs (make-vector label-count '())))
- (let lp ((n 0))
- (when (< n label-count)
- (vector-set!
- defs
- n
- (match (lookup-cont (idx->label n) dfg)
- (($ $kargs _ _ body)
- (match (find-call body)
- (($ $continue k) (cont-defs k))))
- (($ $kreceive arity kargs)
- (cont-defs kargs))
- (($ $kclause arity ($ $cont kargs ($ $kargs names syms)))
- syms)
- (($ $kfun src meta self) (list self))
- (($ $ktail) '())))
- (lp (1+ n))))
- defs))
-
-(define (compute-label-and-var-ranges fun)
- (match fun
- (($ $cont kfun ($ $kfun src meta self))
- ((make-local-cont-folder min-label label-count min-var var-count)
- (lambda (k cont min-label label-count min-var var-count)
- (let ((min-label (min k min-label))
- (label-count (1+ label-count)))
- (match cont
- (($ $kargs names vars body)
- (values min-label label-count
- (fold min min-var vars) (+ var-count (length vars))))
- (($ $kfun src meta self)
- (values min-label label-count (min self min-var) (1+ var-count)))
- (_
- (values min-label label-count min-var var-count)))))
- fun kfun 0 self 0))))
-
-;; Compute a vector containing, for each node, a list of the nodes that
-;; it immediately dominates. These are the "D" edges in the DJ tree.
-
-(define (compute-equivalent-subexpressions fun dfg)
- (define (compute min-label label-count min-var var-count idoms avail effects)
- (let ((defs (compute-defs dfg min-label label-count))
- (var-substs (make-vector var-count #f))
- (equiv-labels (make-vector label-count #f))
- (equiv-set (make-hash-table)))
- (define (idx->label idx) (+ idx min-label))
- (define (label->idx label) (- label min-label))
- (define (idx->var idx) (+ idx min-var))
- (define (var->idx var) (- var min-var))
-
- (define (for-each/2 f l1 l2)
- (unless (= (length l1) (length l2))
- (error "bad lengths" l1 l2))
- (let lp ((l1 l1) (l2 l2))
- (when (pair? l1)
- (f (car l1) (car l2))
- (lp (cdr l1) (cdr l2)))))
-
- (define (subst-var var)
- ;; It could be that the var is free in this function; if so, its
- ;; name will be less than min-var.
- (let ((idx (var->idx var)))
- (if (<= 0 idx)
- (vector-ref var-substs idx)
- var)))
-
- (define (compute-exp-key exp)
- (match exp
- (($ $const val) (cons 'const val))
- (($ $prim name) (cons 'prim name))
- (($ $fun body) #f)
- (($ $rec names syms funs) #f)
- (($ $call proc args) #f)
- (($ $callk k proc args) #f)
- (($ $primcall name args)
- (cons* 'primcall name (map subst-var args)))
- (($ $branch _ ($ $primcall name args))
- (cons* 'primcall name (map subst-var args)))
- (($ $branch) #f)
- (($ $values args) #f)
- (($ $prompt escape? tag handler) #f)))
-
- (define (add-auxiliary-definitions! label exp-key)
- (let ((defs (vector-ref defs (label->idx label))))
- (define (add-def! aux-key var)
- (let ((equiv (hash-ref equiv-set aux-key '())))
- (hash-set! equiv-set aux-key
- (acons label (list var) equiv))))
- (match exp-key
- (('primcall 'box val)
- (match defs
- ((box)
- (add-def! `(primcall box-ref ,(subst-var box)) val))))
- (('primcall 'box-set! box val)
- (add-def! `(primcall box-ref ,box) val))
- (('primcall 'cons car cdr)
- (match defs
- ((pair)
- (add-def! `(primcall car ,(subst-var pair)) car)
- (add-def! `(primcall cdr ,(subst-var pair)) cdr))))
- (('primcall 'set-car! pair car)
- (add-def! `(primcall car ,pair) car))
- (('primcall 'set-cdr! pair cdr)
- (add-def! `(primcall cdr ,pair) cdr))
- (('primcall (or 'make-vector 'make-vector/immediate) len fill)
- (match defs
- ((vec)
- (add-def! `(primcall vector-length ,(subst-var vec)) len))))
- (('primcall 'vector-set! vec idx val)
- (add-def! `(primcall vector-ref ,vec ,idx) val))
- (('primcall 'vector-set!/immediate vec idx val)
- (add-def! `(primcall vector-ref/immediate ,vec ,idx) val))
- (('primcall (or 'allocate-struct 'allocate-struct/immediate)
- vtable size)
- (match defs
- (() #f) ;; allocate-struct in tail or kreceive position.
- ((struct)
- (add-def! `(primcall struct-vtable ,(subst-var struct))
- vtable))))
- (('primcall 'struct-set! struct n val)
- (add-def! `(primcall struct-ref ,struct ,n) val))
- (('primcall 'struct-set!/immediate struct n val)
- (add-def! `(primcall struct-ref/immediate ,struct ,n) val))
- (_ #t))))
-
- ;; The initial substs vector is the identity map.
- (let lp ((var min-var))
- (when (< (var->idx var) var-count)
- (vector-set! var-substs (var->idx var) var)
- (lp (1+ var))))
-
- ;; Traverse the labels in fun in forward order, which will visit
- ;; dominators first.
- (let lp ((label min-label))
- (when (< (label->idx label) label-count)
- (match (lookup-cont label dfg)
- (($ $kargs names vars body)
- (match (find-call body)
- (($ $continue k src exp)
- (let* ((exp-key (compute-exp-key exp))
- (equiv (hash-ref equiv-set exp-key '()))
- (lidx (label->idx label))
- (fx (vector-ref effects lidx))
- (avail (vector-ref avail lidx)))
- (let lp ((candidates equiv))
- (match candidates
- (()
- ;; No matching expressions. Add our expression
- ;; to the equivalence set, if appropriate. Note
- ;; that expressions that allocate a fresh object
- ;; or change the current fluid environment can't
- ;; be eliminated by CSE (though DCE might do it
- ;; if the value proves to be unused, in the
- ;; allocation case).
- (when (and exp-key
- (not (causes-effect? fx &allocation))
- (not (effect-clobbers?
- fx
- (&read-object &fluid))))
- (hash-set! equiv-set exp-key
- (acons label (vector-ref defs lidx)
- equiv))))
- (((and head (candidate . vars)) . candidates)
- (cond
- ((not (intset-ref avail candidate))
- ;; This expression isn't available here; try
- ;; the next one.
- (lp candidates))
- (else
- ;; Yay, a match. Mark expression as equivalent.
- (vector-set! equiv-labels lidx head)
- ;; If we dominate the successor, mark vars
- ;; for substitution.
- (when (= label (vector-ref idoms (label->idx k)))
- (for-each/2
- (lambda (var subst-var)
- (vector-set! var-substs (var->idx var) subst-var))
- (vector-ref defs lidx)
- vars)))))))
- ;; If this expression defines auxiliary definitions,
- ;; as `cons' does for the results of `car' and `cdr',
- ;; define those. Do so after finding equivalent
- ;; expressions, so that we can take advantage of
- ;; subst'd output vars.
- (add-auxiliary-definitions! label exp-key)))))
- (_ #f))
- (lp (1+ label))))
- (values (compute-dom-edges idoms min-label)
- equiv-labels min-label var-substs min-var)))
-
- (call-with-values (lambda () (compute-label-and-var-ranges fun))
- (lambda (min-label label-count min-var var-count)
- (let ((idoms (compute-idoms dfg min-label label-count)))
- (call-with-values
- (lambda ()
- (compute-available-expressions dfg min-label label-count idoms))
- (lambda (avail effects)
- (compute min-label label-count min-var var-count
- idoms avail effects)))))))
-
-(define (apply-cse fun dfg
- doms equiv-labels min-label var-substs min-var boolv)
- (define (idx->label idx) (+ idx min-label))
- (define (label->idx label) (- label min-label))
- (define (idx->var idx) (+ idx min-var))
- (define (var->idx var) (- var min-var))
- (define (true-idx idx) (ash idx 1))
- (define (false-idx idx) (1+ (ash idx 1)))
-
- (define (subst-var var)
- ;; It could be that the var is free in this function; if so,
- ;; its name will be less than min-var.
- (let ((idx (var->idx var)))
- (if (<= 0 idx)
- (vector-ref var-substs idx)
- var)))
-
- (define (visit-fun-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail
- ,(and clause (visit-fun-cont clause)))))
- (($ $cont label ($ $kclause arity ($ $cont kbody body) alternate))
- (label ($kclause ,arity ,(visit-cont kbody body)
- ,(and alternate (visit-fun-cont alternate)))))))
-
- (define (visit-cont label cont)
- (rewrite-cps-cont cont
- (($ $kargs names vars body)
- (label ($kargs names vars ,(visit-term body label))))
- (_ (label ,cont))))
-
- (define (visit-term term label)
- (define (visit-exp exp)
- ;; We shouldn't see $fun here.
- (rewrite-cps-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $call proc args)
- ($call (subst-var proc) ,(map subst-var args)))
- (($ $callk k proc args)
- ($callk k (subst-var proc) ,(map subst-var args)))
- (($ $primcall name args)
- ($primcall name ,(map subst-var args)))
- (($ $branch k exp)
- ($branch k ,(visit-exp exp)))
- (($ $values args)
- ($values ,(map subst-var args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst-var tag) handler))))
-
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(cse body dfg)))))
-
- (define (visit-exp* k src exp)
- (match exp
- (($ $fun)
- (build-cps-term
- ($continue k src ,(visit-fun exp))))
- (($ $rec names syms funs)
- (build-cps-term
- ($continue k src ($rec names syms (map visit-fun funs)))))
- (_
- (cond
- ((vector-ref equiv-labels (label->idx label))
- => (match-lambda
- ((equiv . vars)
- (let* ((eidx (label->idx equiv)))
- (match exp
- (($ $branch kt exp)
- (let* ((bool (vector-ref boolv (label->idx label)))
- (t (intset-ref bool (true-idx eidx)))
- (f (intset-ref bool (false-idx eidx))))
- (if (eqv? t f)
- (build-cps-term
- ($continue k src
- ($branch kt ,(visit-exp exp))))
- (build-cps-term
- ($continue (if t kt k) src ($values ()))))))
- (_
- ;; FIXME: can we always continue with $values? why
- ;; or why not?
- (rewrite-cps-term (lookup-cont k dfg)
- (($ $kargs)
- ($continue k src ($values vars)))
- (_
- ($continue k src ,(visit-exp exp))))))))))
- (else
- (build-cps-term
- ($continue k src ,(visit-exp exp))))))))
-
- (define (visit-dom-conts label)
- (let ((cont (lookup-cont label dfg)))
- (match cont
- (($ $ktail) '())
- (($ $kargs) (list (visit-cont label cont)))
- (else
- (cons (visit-cont label cont)
- (append-map visit-dom-conts
- (vector-ref doms (label->idx label))))))))
-
- (rewrite-cps-term term
- (($ $letk conts body)
- ,(visit-term body label))
- (($ $continue k src exp)
- ,(let ((conts (append-map visit-dom-conts
- (vector-ref doms (label->idx label)))))
- (if (null? conts)
- (visit-exp* k src exp)
- (build-cps-term
- ($letk ,conts ,(visit-exp* k src exp))))))))
-
- (visit-fun-cont fun))
-
-(define (cse fun dfg)
- (call-with-values (lambda () (compute-equivalent-subexpressions fun dfg))
- (lambda (doms equiv-labels min-label var-substs min-var)
- (apply-cse fun dfg doms equiv-labels min-label var-substs min-var
- (compute-truthy-expressions dfg
- min-label (vector-length doms))))))
-
-(define (eliminate-common-subexpressions fun)
- (call-with-values (lambda () (renumber fun))
- (lambda (fun nlabels nvars)
- (cse fun (compute-dfg fun)))))
diff --git a/module/language/cps/dce.scm b/module/language/cps/dce.scm
deleted file mode 100644
index 34ffc3a47..000000000
--- a/module/language/cps/dce.scm
+++ /dev/null
@@ -1,363 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Various optimizations can inline calls from one continuation to some
-;;; other continuation, usually in response to information about the
-;;; return arity of the call. That leaves us with dangling
-;;; continuations that aren't reachable any more from the procedure
-;;; entry. This pass will remove them.
-;;;
-;;; This pass also kills dead expressions: code that has no side
-;;; effects, and whose value is unused. It does so by marking all live
-;;; values, and then discarding other values as dead. This happens
-;;; recursively through procedures, so it should be possible to elide
-;;; dead procedures as well.
-;;;
-;;; Code:
-
-(define-module (language cps dce)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps effects-analysis)
- #:use-module (language cps renumber)
- #:use-module (language cps types)
- #:export (eliminate-dead-code))
-
-(define-record-type $fun-data
- (make-fun-data min-label effects live-conts defs)
- fun-data?
- (min-label fun-data-min-label)
- (effects fun-data-effects)
- (live-conts fun-data-live-conts)
- (defs fun-data-defs))
-
-(define (compute-defs dfg min-label label-count)
- (define (cont-defs k)
- (match (lookup-cont k dfg)
- (($ $kargs names vars) vars)
- (_ #f)))
- (define (idx->label idx) (+ idx min-label))
- (let ((defs (make-vector label-count #f)))
- (let lp ((n 0))
- (when (< n label-count)
- (vector-set!
- defs
- n
- (match (lookup-cont (idx->label n) dfg)
- (($ $kargs _ _ body)
- (match (find-call body)
- (($ $continue k src exp)
- (match exp
- (($ $branch) #f)
- (_ (cont-defs k))))))
- (($ $kreceive arity kargs)
- (cont-defs kargs))
- (($ $kclause arity ($ $cont kargs ($ $kargs names syms)))
- syms)
- (($ $kfun src meta self) (list self))
- (($ $ktail) #f)))
- (lp (1+ n))))
- defs))
-
-(define (elide-type-checks! fun dfg effects min-label label-count)
- (match fun
- (($ $cont kfun ($ $kfun src meta min-var))
- (let ((typev (infer-types fun dfg)))
- (define (idx->label idx) (+ idx min-label))
- (define (var->idx var) (- var min-var))
- (define (visit-primcall lidx fx name args)
- (when (primcall-types-check? typev (idx->label lidx) name args)
- (vector-set! effects lidx
- (logand fx (lognot &type-check)))))
- (let lp ((lidx 0))
- (when (< lidx label-count)
- (let ((fx (vector-ref effects lidx)))
- (unless (causes-all-effects? fx)
- (when (causes-effect? fx &type-check)
- (match (lookup-cont (idx->label lidx) dfg)
- (($ $kargs _ _ term)
- (match (find-call term)
- (($ $continue k src ($ $primcall name args))
- (visit-primcall lidx fx name args))
- (($ $continue k src ($ $branch _ ($primcall name args)))
- (visit-primcall lidx fx name args))
- (_ #f)))
- (_ #f)))))
- (lp (1+ lidx))))))))
-
-(define (compute-live-code fun)
- (let* ((fun-data-table (make-hash-table))
- (dfg (compute-dfg fun #:global? #t))
- (live-vars (make-bitvector (dfg-var-count dfg) #f))
- (changed? #f))
- (define (mark-live! var)
- (unless (value-live? var)
- (set! changed? #t)
- (bitvector-set! live-vars var #t)))
- (define (value-live? var)
- (bitvector-ref live-vars var))
- (define (ensure-fun-data fun)
- (or (hashq-ref fun-data-table fun)
- (call-with-values (lambda ()
- ((make-local-cont-folder label-count max-label)
- (lambda (k cont label-count max-label)
- (values (1+ label-count) (max k max-label)))
- fun 0 -1))
- (lambda (label-count max-label)
- (let* ((min-label (- (1+ max-label) label-count))
- (effects (compute-effects dfg min-label label-count))
- (live-conts (make-bitvector label-count #f))
- (defs (compute-defs dfg min-label label-count))
- (fun-data (make-fun-data
- min-label effects live-conts defs)))
- (elide-type-checks! fun dfg effects min-label label-count)
- (hashq-set! fun-data-table fun fun-data)
- (set! changed? #t)
- fun-data)))))
- (define (visit-fun fun)
- (match (ensure-fun-data fun)
- (($ $fun-data min-label effects live-conts defs)
- (define (idx->label idx) (+ idx min-label))
- (define (label->idx label) (- label min-label))
- (define (known-allocation? var dfg)
- (match (lookup-predecessors (lookup-def var dfg) dfg)
- ((def-exp-k)
- (match (lookup-cont def-exp-k dfg)
- (($ $kargs _ _ term)
- (match (find-call term)
- (($ $continue k src ($ $values (var)))
- (known-allocation? var dfg))
- (($ $continue k src ($ $primcall))
- (let ((kidx (label->idx def-exp-k)))
- (and (>= kidx 0)
- (causes-effect? (vector-ref effects kidx)
- &allocation))))
- (_ #f)))
- (_ #f)))
- (_ #f)))
- (define (visit-grey-exp n exp)
- (let ((defs (vector-ref defs n))
- (fx (vector-ref effects n)))
- (or
- ;; No defs; perhaps continuation is $ktail.
- (not defs)
- ;; Do we have a live def?
- (or-map value-live? defs)
- ;; Does this expression cause all effects? If so, it's
- ;; definitely live.
- (causes-all-effects? fx)
- ;; Does it cause a type check, but we weren't able to
- ;; prove that the types check?
- (causes-effect? fx &type-check)
- ;; We might have a setter. If the object being assigned
- ;; to is live or was not created by us, then this
- ;; expression is live. Otherwise the value is still dead.
- (and (causes-effect? fx &write)
- (match exp
- (($ $primcall
- (or 'vector-set! 'vector-set!/immediate
- 'set-car! 'set-cdr!
- 'box-set!)
- (obj . _))
- (or (value-live? obj)
- (not (known-allocation? obj dfg))))
- (_ #t))))))
- (let lp ((n (1- (vector-length effects))))
- (unless (< n 0)
- (let ((cont (lookup-cont (idx->label n) dfg)))
- (match cont
- (($ $kargs _ _ body)
- (let lp ((body body))
- (match body
- (($ $letk conts body) (lp body))
- (($ $continue k src exp)
- (unless (bitvector-ref live-conts n)
- (when (visit-grey-exp n exp)
- (set! changed? #t)
- (bitvector-set! live-conts n #t)))
- (when (bitvector-ref live-conts n)
- (match exp
- ((or ($ $const) ($ $prim))
- #f)
- (($ $fun body)
- (visit-fun body))
- (($ $rec names syms funs)
- (for-each (lambda (sym fun)
- (when (value-live? sym)
- (match fun
- (($ $fun body)
- (visit-fun body)))))
- syms funs))
- (($ $prompt escape? tag handler)
- (mark-live! tag))
- (($ $call proc args)
- (mark-live! proc)
- (for-each mark-live! args))
- (($ $callk k proc args)
- (mark-live! proc)
- (for-each mark-live! args))
- (($ $primcall name args)
- (for-each mark-live! args))
- (($ $branch k ($ $primcall name args))
- (for-each mark-live! args))
- (($ $branch k ($ $values (arg)))
- (mark-live! arg))
- (($ $values args)
- (match (vector-ref defs n)
- (#f (for-each mark-live! args))
- (defs (for-each (lambda (use def)
- (when (value-live? def)
- (mark-live! use)))
- args defs))))))))))
- (($ $kreceive arity kargs) #f)
- (($ $kclause arity ($ $cont kargs ($ $kargs names syms body)))
- (for-each mark-live! syms))
- (($ $kfun src meta self)
- (mark-live! self))
- (($ $ktail) #f))
- (lp (1- n))))))))
- (unless (= (dfg-var-count dfg) (var-counter))
- (error "internal error" (dfg-var-count dfg) (var-counter)))
- (let lp ()
- (set! changed? #f)
- (visit-fun fun)
- (when changed? (lp)))
- (values fun-data-table live-vars)))
-
-(define (process-eliminations fun fun-data-table live-vars)
- (define (value-live? var)
- (bitvector-ref live-vars var))
- (define (make-adaptor name k defs)
- (let* ((names (map (lambda (_) 'tmp) defs))
- (syms (map (lambda (_) (fresh-var)) defs))
- (live (filter-map (lambda (def sym)
- (and (value-live? def)
- sym))
- defs syms)))
- (build-cps-cont
- (name ($kargs names syms
- ($continue k #f ($values live)))))))
- (define (visit-fun fun)
- (match (hashq-ref fun-data-table fun)
- (($ $fun-data min-label effects live-conts defs)
- (define (label->idx label) (- label min-label))
- (define (visit-cont cont)
- (match (visit-cont* cont)
- ((cont) cont)))
- (define (visit-cont* cont)
- (match cont
- (($ $cont label cont)
- (match cont
- (($ $kargs names syms body)
- (match (filter-map (lambda (name sym)
- (and (value-live? sym)
- (cons name sym)))
- names syms)
- (((names . syms) ...)
- (list
- (build-cps-cont
- (label ($kargs names syms
- ,(visit-term body label))))))))
- (($ $kfun src meta self tail clause)
- (list
- (build-cps-cont
- (label ($kfun src meta self ,tail
- ,(and clause (visit-cont clause)))))))
- (($ $kclause arity body alternate)
- (list
- (build-cps-cont
- (label ($kclause ,arity
- ,(visit-cont body)
- ,(and alternate
- (visit-cont alternate)))))))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (let ((defs (vector-ref defs (label->idx label))))
- (if (and-map value-live? defs)
- (list (build-cps-cont (label ,cont)))
- (let-fresh (adapt) ()
- (list (make-adaptor adapt kargs defs)
- (build-cps-cont
- (label ($kreceive req rest adapt))))))))
- (_ (list (build-cps-cont (label ,cont))))))))
- (define (visit-conts conts)
- (append-map visit-cont* conts))
- (define (visit-term term term-k)
- (match term
- (($ $letk conts body)
- (let ((body (visit-term body term-k)))
- (match (visit-conts conts)
- (() body)
- (conts (build-cps-term ($letk ,conts ,body))))))
- (($ $continue k src ($ $values args))
- (match (vector-ref defs (label->idx term-k))
- (#f term)
- (defs
- (let ((args (filter-map (lambda (use def)
- (and (value-live? def) use))
- args defs)))
- (build-cps-term
- ($continue k src ($values args)))))))
- (($ $continue k src exp)
- (if (bitvector-ref live-conts (label->idx term-k))
- (match exp
- (($ $fun body)
- (build-cps-term
- ($continue k src ($fun ,(visit-fun body)))))
- (($ $rec names syms funs)
- (rewrite-cps-term
- (filter-map
- (lambda (name sym fun)
- (and (value-live? sym)
- (match fun
- (($ $fun body)
- (list name
- sym
- (build-cps-exp
- ($fun ,(visit-fun body))))))))
- names syms funs)
- (()
- ($continue k src ($values ())))
- (((names syms funs) ...)
- ($continue k src ($rec names syms funs)))))
- (_
- (match (vector-ref defs (label->idx term-k))
- ((or #f ((? value-live?) ...))
- (build-cps-term
- ($continue k src ,exp)))
- (syms
- (let-fresh (adapt) ()
- (build-cps-term
- ($letk (,(make-adaptor adapt k syms))
- ($continue adapt src ,exp))))))))
- (build-cps-term ($continue k src ($values ())))))))
- (visit-cont fun))))
- (visit-fun fun))
-
-(define (eliminate-dead-code fun)
- (call-with-values (lambda () (renumber fun))
- (lambda (fun nlabels nvars)
- (parameterize ((label-counter nlabels)
- (var-counter nvars))
- (call-with-values (lambda () (compute-live-code fun))
- (lambda (fun-data-table live-vars)
- (process-eliminations fun fun-data-table live-vars)))))))
diff --git a/module/language/cps/effects-analysis.scm b/module/language/cps/effects-analysis.scm
deleted file mode 100644
index 7a49f869f..000000000
--- a/module/language/cps/effects-analysis.scm
+++ /dev/null
@@ -1,499 +0,0 @@
-;;; Effects analysis on CPS
-
-;; Copyright (C) 2011, 2012, 2013, 2014, 2015 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:
-;;;
-;;; A helper module to compute the set of effects caused by an
-;;; expression. This information is useful when writing algorithms that
-;;; move code around, while preserving the semantics of an input
-;;; program.
-;;;
-;;; The effects set is represented as an integer with three parts. The
-;;; low 4 bits indicate effects caused by an expression, as a bitfield.
-;;; The next 4 bits indicate the kind of memory accessed by the
-;;; expression, if it accesses mutable memory. Finally the rest of the
-;;; bits indicate the field in the object being accessed, if known, or
-;;; -1 for unknown.
-;;;
-;;; In this way we embed a coarse type-based alias analysis in the
-;;; effects analysis. For example, a "car" call is modelled as causing
-;;; a read to field 0 on a &pair, and causing a &type-check effect. If
-;;; any intervening code sets the car of any pair, that will block
-;;; motion of the "car" call, because any write to field 0 of a pair is
-;;; seen by effects analysis as being a write to field 0 of all pairs.
-;;;
-;;; Code:
-
-(define-module (language cps effects-analysis)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (ice-9 match)
- #:export (expression-effects
- compute-effects
- synthesize-definition-effects!
-
- &allocation
- &type-check
- &read
- &write
-
- &fluid
- &prompt
- &car
- &cdr
- &vector
- &box
- &module
- &struct
- &string
- &bytevector
-
- &object
- &field
-
- &allocate
- &read-object
- &read-field
- &write-object
- &write-field
-
- &no-effects
- &all-effects
-
- exclude-effects
- effect-free?
- constant?
- causes-effect?
- causes-all-effects?
- effect-clobbers?))
-
-(define-syntax define-flags
- (lambda (x)
- (syntax-case x ()
- ((_ all shift name ...)
- (let ((count (length #'(name ...))))
- (with-syntax (((n ...) (iota count))
- (count count))
- #'(begin
- (define-syntax name (identifier-syntax (ash 1 n)))
- ...
- (define-syntax all (identifier-syntax (1- (ash 1 count))))
- (define-syntax shift (identifier-syntax count)))))))))
-
-(define-syntax define-enumeration
- (lambda (x)
- (define (count-bits n)
- (let lp ((out 1))
- (if (< n (ash 1 (1- out)))
- out
- (lp (1+ out)))))
- (syntax-case x ()
- ((_ mask shift name ...)
- (let* ((len (length #'(name ...)))
- (bits (count-bits len)))
- (with-syntax (((n ...) (iota len))
- (bits bits))
- #'(begin
- (define-syntax name (identifier-syntax n))
- ...
- (define-syntax mask (identifier-syntax (1- (ash 1 bits))))
- (define-syntax shift (identifier-syntax bits)))))))))
-
-(define-flags &all-effect-kinds &effect-kind-bits
- ;; Indicates that an expression may cause a type check. A type check,
- ;; for the purposes of this analysis, is the possibility of throwing
- ;; an exception the first time an expression is evaluated. If the
- ;; expression did not cause an exception to be thrown, users can
- ;; assume that evaluating the expression again will not cause an
- ;; exception to be thrown.
- ;;
- ;; For example, (+ x y) might throw if X or Y are not numbers. But if
- ;; it doesn't throw, it should be safe to elide a dominated, common
- ;; subexpression (+ x y).
- &type-check
-
- ;; Indicates that an expression may return a fresh object. The kind
- ;; of object is indicated in the object kind field.
- &allocation
-
- ;; Indicates that an expression may cause a read from memory. The
- ;; kind of memory is given in the object kind field. Some object
- ;; kinds have finer-grained fields; those are expressed in the "field"
- ;; part of the effects value. -1 indicates "the whole object".
- &read
-
- ;; Indicates that an expression may cause a write to memory.
- &write)
-
-(define-enumeration &memory-kind-mask &memory-kind-bits
- ;; Indicates than an expression may access unknown kinds of memory.
- &unknown-memory-kinds
-
- ;; Indicates that an expression depends on the value of a fluid
- ;; variable, or on the current fluid environment.
- &fluid
-
- ;; Indicates that an expression depends on the current prompt
- ;; stack.
- &prompt
-
- ;; Indicates that an expression depends on the value of the car or cdr
- ;; of a pair.
- &pair
-
- ;; Indicates that an expression depends on the value of a vector
- ;; field. The effect field indicates the specific field, or zero for
- ;; an unknown field.
- &vector
-
- ;; Indicates that an expression depends on the value of a variable
- ;; cell.
- &box
-
- ;; Indicates that an expression depends on the current module.
- &module
-
- ;; Indicates that an expression depends on the value of a struct
- ;; field. The effect field indicates the specific field, or zero for
- ;; an unknown field.
- &struct
-
- ;; Indicates that an expression depends on the contents of a string.
- &string
-
- ;; Indicates that an expression depends on the contents of a
- ;; bytevector. We cannot be more precise, as bytevectors may alias
- ;; other bytevectors.
- &bytevector)
-
-(define-inlinable (&field kind field)
- (ash (logior (ash field &memory-kind-bits) kind) &effect-kind-bits))
-(define-inlinable (&object kind)
- (&field kind -1))
-
-(define-inlinable (&allocate kind)
- (logior &allocation (&object kind)))
-(define-inlinable (&read-field kind field)
- (logior &read (&field kind field)))
-(define-inlinable (&read-object kind)
- (logior &read (&object kind)))
-(define-inlinable (&write-field kind field)
- (logior &write (&field kind field)))
-(define-inlinable (&write-object kind)
- (logior &write (&object kind)))
-
-(define-syntax &no-effects (identifier-syntax 0))
-(define-syntax &all-effects
- (identifier-syntax
- (logior &all-effect-kinds (&object &unknown-memory-kinds))))
-
-(define-inlinable (constant? effects)
- (zero? effects))
-
-(define-inlinable (causes-effect? x effects)
- (not (zero? (logand x effects))))
-
-(define-inlinable (causes-all-effects? x)
- (eqv? x &all-effects))
-
-(define (effect-clobbers? a b)
- "Return true if A clobbers B. This is the case if A is a write, and B
-is or might be a read or a write to the same location as A."
- (define (locations-same?)
- (let ((a (ash a (- &effect-kind-bits)))
- (b (ash b (- &effect-kind-bits))))
- (or (eqv? &unknown-memory-kinds (logand a &memory-kind-mask))
- (eqv? &unknown-memory-kinds (logand b &memory-kind-mask))
- (and (eqv? (logand a &memory-kind-mask) (logand b &memory-kind-mask))
- ;; A negative field indicates "the whole object".
- ;; Non-negative fields indicate only part of the object.
- (or (< a 0) (< b 0) (= a b))))))
- (and (not (zero? (logand a &write)))
- (not (zero? (logand b (logior &read &write))))
- (locations-same?)))
-
-(define (lookup-constant-index sym dfg)
- (call-with-values (lambda () (find-constant-value sym dfg))
- (lambda (has-const? val)
- (and has-const? (integer? val) (exact? val) (<= 0 val) val))))
-
-(define-inlinable (indexed-field kind n dfg)
- (cond
- ((lookup-constant-index n dfg)
- => (lambda (idx)
- (&field kind idx)))
- (else (&object kind))))
-
-(define *primitive-effects* (make-hash-table))
-
-(define-syntax-rule (define-primitive-effects* dfg
- ((name . args) effects ...)
- ...)
- (begin
- (hashq-set! *primitive-effects* 'name
- (case-lambda*
- ((dfg . args) (logior effects ...))
- (_ &all-effects)))
- ...))
-
-(define-syntax-rule (define-primitive-effects ((name . args) effects ...) ...)
- (define-primitive-effects* dfg ((name . args) effects ...) ...))
-
-;; Miscellaneous.
-(define-primitive-effects
- ((values . _)))
-
-;; Generic effect-free predicates.
-(define-primitive-effects
- ((eq? . _))
- ((eqv? . _))
- ((equal? . _))
- ((pair? arg))
- ((null? arg))
- ((nil? arg ))
- ((symbol? arg))
- ((variable? arg))
- ((vector? arg))
- ((struct? arg))
- ((string? arg))
- ((number? arg))
- ((char? arg))
- ((bytevector? arg))
- ((keyword? arg))
- ((bitvector? arg))
- ((procedure? arg))
- ((thunk? arg)))
-
-;; Fluids.
-(define-primitive-effects
- ((fluid-ref f) (&read-object &fluid) &type-check)
- ((fluid-set! f v) (&write-object &fluid) &type-check)
- ((push-fluid f v) (&write-object &fluid) &type-check)
- ((pop-fluid) (&write-object &fluid) &type-check))
-
-;; Prompts.
-(define-primitive-effects
- ((make-prompt-tag #:optional arg) (&allocate &unknown-memory-kinds)))
-
-;; Pairs.
-(define-primitive-effects
- ((cons a b) (&allocate &pair))
- ((list . _) (&allocate &pair))
- ((car x) (&read-field &pair 0) &type-check)
- ((set-car! x y) (&write-field &pair 0) &type-check)
- ((cdr x) (&read-field &pair 1) &type-check)
- ((set-cdr! x y) (&write-field &pair 1) &type-check)
- ((memq x y) (&read-object &pair) &type-check)
- ((memv x y) (&read-object &pair) &type-check)
- ((list? arg) (&read-field &pair 1))
- ((length l) (&read-field &pair 1) &type-check))
-
-;; Variables.
-(define-primitive-effects
- ((box v) (&allocate &box))
- ((box-ref v) (&read-object &box) &type-check)
- ((box-set! v x) (&write-object &box) &type-check))
-
-;; Vectors.
-(define (vector-field n dfg)
- (indexed-field &vector n dfg))
-(define (read-vector-field n dfg)
- (logior &read (vector-field n dfg)))
-(define (write-vector-field n dfg)
- (logior &write (vector-field n dfg)))
-(define-primitive-effects* dfg
- ((vector . _) (&allocate &vector))
- ((make-vector n init) (&allocate &vector) &type-check)
- ((make-vector/immediate n init) (&allocate &vector))
- ((vector-ref v n) (read-vector-field n dfg) &type-check)
- ((vector-ref/immediate v n) (read-vector-field n dfg) &type-check)
- ((vector-set! v n x) (write-vector-field n dfg) &type-check)
- ((vector-set!/immediate v n x) (write-vector-field n dfg) &type-check)
- ((vector-length v) &type-check))
-
-;; Structs.
-(define (struct-field n dfg)
- (indexed-field &struct n dfg))
-(define (read-struct-field n dfg)
- (logior &read (struct-field n dfg)))
-(define (write-struct-field n dfg)
- (logior &write (struct-field n dfg)))
-(define-primitive-effects* dfg
- ((allocate-struct vt n) (&allocate &struct) &type-check)
- ((allocate-struct/immediate v n) (&allocate &struct) &type-check)
- ((make-struct vt ntail . _) (&allocate &struct) &type-check)
- ((make-struct/no-tail vt . _) (&allocate &struct) &type-check)
- ((struct-ref s n) (read-struct-field n dfg) &type-check)
- ((struct-ref/immediate s n) (read-struct-field n dfg) &type-check)
- ((struct-set! s n x) (write-struct-field n dfg) &type-check)
- ((struct-set!/immediate s n x) (write-struct-field n dfg) &type-check)
- ((struct-vtable s) &type-check))
-
-;; Strings.
-(define-primitive-effects
- ((string-ref s n) (&read-object &string) &type-check)
- ((string-set! s n c) (&write-object &string) &type-check)
- ((number->string _) (&allocate &string) &type-check)
- ((string->number _) (&read-object &string) &type-check)
- ((string-length s) &type-check))
-
-;; Bytevectors.
-(define-primitive-effects
- ((bytevector-length _) &type-check)
-
- ((bv-u8-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s8-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u16-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s16-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u64-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s64-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-f32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-f64-ref bv n) (&read-object &bytevector) &type-check)
-
- ((bv-u8-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s8-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u16-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s16-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u64-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s64-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-f32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-f64-set! bv n x) (&write-object &bytevector) &type-check))
-
-;; Modules.
-(define-primitive-effects
- ((current-module) (&read-object &module))
- ((cache-current-module! m scope) (&write-object &box))
- ((resolve name bound?) (&read-object &module) &type-check)
- ((cached-toplevel-box scope name bound?) &type-check)
- ((cached-module-box mod name public? bound?) &type-check)
- ((define! name val) (&read-object &module) (&write-object &box)))
-
-;; Numbers.
-(define-primitive-effects
- ((= . _) &type-check)
- ((< . _) &type-check)
- ((> . _) &type-check)
- ((<= . _) &type-check)
- ((>= . _) &type-check)
- ((zero? . _) &type-check)
- ((add . _) &type-check)
- ((mul . _) &type-check)
- ((sub . _) &type-check)
- ((div . _) &type-check)
- ((sub1 . _) &type-check)
- ((add1 . _) &type-check)
- ((quo . _) &type-check)
- ((rem . _) &type-check)
- ((mod . _) &type-check)
- ((complex? _) &type-check)
- ((real? _) &type-check)
- ((rational? _) &type-check)
- ((inf? _) &type-check)
- ((nan? _) &type-check)
- ((integer? _) &type-check)
- ((exact? _) &type-check)
- ((inexact? _) &type-check)
- ((even? _) &type-check)
- ((odd? _) &type-check)
- ((ash n m) &type-check)
- ((logand . _) &type-check)
- ((logior . _) &type-check)
- ((logxor . _) &type-check)
- ((lognot . _) &type-check)
- ((logtest a b) &type-check)
- ((logbit? a b) &type-check)
- ((sqrt _) &type-check)
- ((abs _) &type-check))
-
-;; Characters.
-(define-primitive-effects
- ((char . _) &type-check)
- ((char<=? . _) &type-check)
- ((char>=? . _) &type-check)
- ((char>? . _) &type-check)
- ((integer->char _) &type-check)
- ((char->integer _) &type-check))
-
-(define (primitive-effects dfg name args)
- (let ((proc (hashq-ref *primitive-effects* name)))
- (if proc
- (apply proc dfg args)
- &all-effects)))
-
-(define (expression-effects exp dfg)
- (match exp
- ((or ($ $const) ($ $prim) ($ $values))
- &no-effects)
- ((or ($ $fun) ($ $rec))
- (&allocate &unknown-memory-kinds))
- (($ $prompt)
- (&write-object &prompt))
- ((or ($ $call) ($ $callk))
- &all-effects)
- (($ $branch k exp)
- (expression-effects exp dfg))
- (($ $primcall name args)
- (primitive-effects dfg name args))))
-
-(define* (compute-effects dfg #:optional (min-label (dfg-min-label dfg))
- (label-count (dfg-label-count dfg)))
- (let ((effects (make-vector label-count &no-effects)))
- (define (idx->label idx) (+ idx min-label))
- (let lp ((n 0))
- (when (< n label-count)
- (vector-set!
- effects
- n
- (match (lookup-cont (idx->label n) dfg)
- (($ $kargs names syms body)
- (expression-effects (find-expression body) dfg))
- (($ $kreceive arity kargs)
- (match arity
- (($ $arity _ () #f () #f) &type-check)
- (($ $arity () () _ () #f) (&allocate &pair))
- (($ $arity _ () _ () #f) (logior (&allocate &pair) &type-check))))
- (($ $kfun) &type-check)
- (($ $kclause) &type-check)
- (($ $ktail) &no-effects)))
- (lp (1+ n))))
- effects))
-
-;; There is a way to abuse effects analysis in CSE to also do scalar
-;; replacement, effectively adding `car' and `cdr' expressions to `cons'
-;; expressions, and likewise with other constructors and setters. This
-;; routine adds appropriate effects to `cons' and `set-car!' and the
-;; like.
-;;
-;; This doesn't affect CSE's ability to eliminate expressions, given
-;; that allocations aren't eliminated anyway, and the new effects will
-;; just cause the allocations not to commute with e.g. set-car! which
-;; is what we want anyway.
-(define* (synthesize-definition-effects! effects dfg min-label #:optional
- (label-count (vector-length effects)))
- (define (label->idx label) (- label min-label))
- (let lp ((label min-label))
- (when (< label (+ min-label label-count))
- (let* ((lidx (label->idx label))
- (fx (vector-ref effects lidx)))
- (unless (zero? (logand (logior &write &allocation) fx))
- (vector-set! effects lidx (logior (vector-ref effects lidx) &read)))
- (lp (1+ label))))))
diff --git a/module/language/cps/elide-values.scm b/module/language/cps/elide-values.scm
deleted file mode 100644
index dadbd403a..000000000
--- a/module/language/cps/elide-values.scm
+++ /dev/null
@@ -1,109 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Primcalls that don't correspond to VM instructions are treated as if
-;;; they are calls, and indeed the later reify-primitives pass turns
-;;; them into calls. Because no return arity checking is done for these
-;;; primitives, if a later optimization pass simplifies the primcall to
-;;; a VM operation, the tail of the simplification has to be a
-;;; primcall to 'values. Most of these primcalls can be elided, and
-;;; that is the job of this pass.
-;;;
-;;; Code:
-
-(define-module (language cps elide-values)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:export (elide-values))
-
-(define (elide-values* fun conts)
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont sym ($ $kargs names syms body))
- (sym ($kargs names syms ,(visit-term body))))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (sym ($kfun src meta self ,tail ,(and clause (visit-cont clause)))))
- (($ $cont sym ($ $kclause arity body alternate))
- (sym ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont)
- ,cont)))
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts)
- ,(visit-term body)))
- (($ $continue k src ($ $primcall 'values vals))
- ,(rewrite-cps-term (vector-ref conts k)
- (($ $ktail)
- ($continue k src ($values vals)))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- ,(cond
- ((and (not rest) (= (length vals) (length req)))
- (build-cps-term
- ($continue kargs src ($values vals))))
- ((and rest (>= (length vals) (length req)))
- (let-fresh (krest) (rest)
- (let ((vals* (append (list-head vals (length req))
- (list rest))))
- (build-cps-term
- ($letk ((krest ($kargs ('rest) (rest)
- ($continue kargs src
- ($values vals*)))))
- ,(let lp ((tail (list-tail vals (length req)))
- (k krest))
- (match tail
- (()
- (build-cps-term ($continue k src
- ($const '()))))
- ((v . tail)
- (let-fresh (krest) (rest)
- (build-cps-term
- ($letk ((krest ($kargs ('rest) (rest)
- ($continue k src
- ($primcall 'cons
- (v rest))))))
- ,(lp tail krest))))))))))))
- (else term)))
- (($ $kargs args)
- ,(if (< (length vals) (length args))
- term
- (let ((vals (list-head vals (length args))))
- (build-cps-term
- ($continue k src ($values vals))))))))
- (($ $continue k src (and fun ($ $fun)))
- ($continue k src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names syms funs))
- ($continue k src ($rec names syms (map visit-fun funs))))
- (($ $continue)
- ,term)))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun cont)
- ($fun ,(visit-cont cont)))))
-
- (visit-cont fun))
-
-(define (elide-values fun)
- (with-fresh-name-state fun
- (let ((conts (build-cont-table fun)))
- (elide-values* fun conts))))
diff --git a/module/language/cps/prune-bailouts.scm b/module/language/cps/prune-bailouts.scm
deleted file mode 100644
index c224f4531..000000000
--- a/module/language/cps/prune-bailouts.scm
+++ /dev/null
@@ -1,101 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass that prunes successors of expressions that bail out.
-;;;
-;;; Code:
-
-(define-module (language cps prune-bailouts)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:export (prune-bailouts))
-
-(define (module-box src module name public? bound? val-proc)
- (let-fresh (kbox) (module-sym name-sym public?-sym bound?-sym box)
- (build-cps-term
- ($letconst (('module module-sym module)
- ('name name-sym name)
- ('public? public?-sym public?)
- ('bound? bound?-sym bound?))
- ($letk ((kbox ($kargs ('box) (box) ,(val-proc box))))
- ($continue kbox src
- ($primcall 'cached-module-box
- (module-sym name-sym public?-sym bound?-sym))))))))
-
-(define (primitive-ref name k src)
- (module-box #f '(guile) name #f #t
- (lambda (box)
- (build-cps-term
- ($continue k src ($primcall 'box-ref (box)))))))
-
-(define (prune-bailouts* fun)
- (define (visit-cont cont ktail)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names vars body))
- (label ($kargs names vars ,(visit-term body ktail))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail
- ,(and clause (visit-cont clause ktail)))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body ktail)
- ,(and alternate (visit-cont alternate ktail)))))
- (_ ,cont)))
-
- (define (visit-term term ktail)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map (lambda (cont) (visit-cont cont ktail)) conts)
- ,(visit-term body ktail)))
- (($ $continue k src exp)
- ,(visit-exp k src exp ktail))))
-
- (define (visit-exp k src exp ktail)
- (rewrite-cps-term exp
- (($ $fun) ($continue k src ,(visit-fun exp)))
- (($ $rec names vars funs)
- ($continue k src ($rec names vars (map visit-fun funs))))
- (($ $primcall (and name (or 'error 'scm-error 'throw)) args)
- ,(if (eq? k ktail)
- (build-cps-term ($continue k src ,exp))
- (let-fresh (kprim kresult kreceive) (prim rest)
- (build-cps-term
- ($letk ((kresult ($kargs ('rest) (rest)
- ($continue ktail src ($values ()))))
- (kreceive ($kreceive '() 'rest kresult))
- (kprim ($kargs ('prim) (prim)
- ($continue kreceive src
- ($call prim args)))))
- ,(primitive-ref name kprim src))))))
- (_ ($continue k src ,exp))))
-
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(prune-bailouts* body)))))
-
- (rewrite-cps-cont fun
- (($ $cont kfun
- ($ $kfun src meta self ($ $cont ktail ($ $ktail)) clause))
- (kfun ($kfun src meta self (ktail ($ktail))
- ,(and clause (visit-cont clause ktail)))))))
-
-(define (prune-bailouts fun)
- (with-fresh-name-state fun
- (prune-bailouts* fun)))
diff --git a/module/language/cps/prune-top-level-scopes.scm b/module/language/cps/prune-top-level-scopes.scm
deleted file mode 100644
index 4839b71f7..000000000
--- a/module/language/cps/prune-top-level-scopes.scm
+++ /dev/null
@@ -1,114 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2014, 2015 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:
-;;;
-;;; A simple pass to prune unneeded top-level scopes.
-;;;
-;;; Code:
-
-(define-module (language cps prune-top-level-scopes)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:export (prune-top-level-scopes))
-
-(define (compute-referenced-scopes fun)
- (let ((scope-name->used? (make-hash-table))
- (scope-var->used? (make-hash-table))
- (k->scope-var (make-hash-table)))
- ;; Visit uses before defs. That way we know when visiting defs
- ;; whether the scope is used or not.
- (define (visit-cont cont)
- (match cont
- (($ $cont k ($ $kargs (name) (var) body))
- (visit-term body)
- (when (hashq-get-handle scope-var->used? var)
- (hashq-set! k->scope-var k var)))
- (($ $cont k ($ $kargs names syms body))
- (visit-term body))
- (($ $cont k ($ $kfun src meta self tail clause))
- (when clause (visit-cont clause)))
- (($ $cont k ($ $kclause arity body alternate))
- (visit-cont body)
- (when alternate (visit-cont alternate)))
- (($ $cont k ($ $kreceive))
- #t)))
- (define (visit-term term)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body))
- (($ $continue k src exp)
- (match exp
- (($ $fun) (visit-fun exp))
- (($ $rec names syms funs)
- (for-each visit-fun funs))
- (($ $primcall 'cached-toplevel-box (scope name bound?))
- (hashq-set! scope-var->used? scope #t))
- (($ $primcall 'cache-current-module! (module scope))
- (hashq-set! scope-var->used? scope #f))
- (($ $const val)
- ;; If there is an entry in the table for "k", it means "val"
- ;; is a scope symbol, bound for use by cached-toplevel-box
- ;; or cache-current-module!, or possibly both (though this
- ;; is not currently the case).
- (and=> (hashq-ref k->scope-var k)
- (lambda (scope-var)
- (when (hashq-ref scope-var->used? scope-var)
- ;; We have a use via cached-toplevel-box. Mark
- ;; this scope as used.
- (hashq-set! scope-name->used? val #t))
- (when (and (hashq-ref scope-name->used? val)
- (not (hashq-ref scope-var->used? scope-var)))
- ;; There is a use, and this sym is used by
- ;; cache-current-module!.
- (hashq-set! scope-var->used? scope-var #t)))))
- (_ #t)))))
- (define (visit-fun fun)
- (match fun
- (($ $fun body)
- (visit-cont body))))
-
- (visit-cont fun)
- scope-var->used?))
-
-(define (prune-top-level-scopes fun)
- (let ((scope-var->used? (compute-referenced-scopes fun)))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont sym ($ $kargs names syms body))
- (sym ($kargs names syms ,(visit-term body))))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (sym ($kfun src meta self ,tail ,(and clause (visit-cont clause)))))
- (($ $cont sym ($ $kclause arity body alternate))
- (sym ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont sym ($ $kreceive))
- ,cont)))
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts) ,(visit-term body)))
- (($ $continue k src
- (and ($ $primcall 'cache-current-module! (module scope))
- (? (lambda _
- (not (hashq-ref scope-var->used? scope))))))
- ($continue k src ($primcall 'values ())))
- (($ $continue)
- ,term)))
- (visit-cont fun)))
diff --git a/module/language/cps/self-references.scm b/module/language/cps/self-references.scm
deleted file mode 100644
index 45e2389ff..000000000
--- a/module/language/cps/self-references.scm
+++ /dev/null
@@ -1,79 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass that prunes successors of expressions that bail out.
-;;;
-;;; Code:
-
-(define-module (language cps self-references)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:export (resolve-self-references))
-
-(define* (resolve-self-references fun #:optional (env '()))
- (define (subst var)
- (or (assq-ref env var) var))
-
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names vars body))
- (label ($kargs names vars ,(visit-term body))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail
- ,(and clause (visit-cont clause)))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (_ ,cont)))
-
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts)
- ,(visit-term body)))
- (($ $continue k src exp)
- ($continue k src ,(visit-exp exp)))))
-
- (define (visit-exp exp)
- (rewrite-cps-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $fun body)
- ($fun ,(resolve-self-references body env)))
- (($ $rec names vars funs)
- ($rec names vars (map visit-recursive-fun funs vars)))
- (($ $call proc args)
- ($call (subst proc) ,(map subst args)))
- (($ $callk k proc args)
- ($callk k (subst proc) ,(map subst args)))
- (($ $primcall name args)
- ($primcall name ,(map subst args)))
- (($ $branch k exp)
- ($branch k ,(visit-exp exp)))
- (($ $values args)
- ($values ,(map subst args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst tag) handler))))
-
- (define (visit-recursive-fun fun var)
- (rewrite-cps-exp fun
- (($ $fun (and cont ($ $cont _ ($ $kfun src meta self))))
- ($fun ,(resolve-self-references cont (acons var self env))))))
-
- (visit-cont fun))
diff --git a/module/language/cps/simplify.scm b/module/language/cps/simplify.scm
deleted file mode 100644
index 10e9d0aa2..000000000
--- a/module/language/cps/simplify.scm
+++ /dev/null
@@ -1,328 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; The fundamental lambda calculus reductions, like beta and eta
-;;; reduction and so on. Pretty lame currently.
-;;;
-;;; Code:
-
-(define-module (language cps simplify)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps renumber)
- #:export (simplify))
-
-(define (compute-eta-reductions fun)
- (let ((table (make-hash-table)))
- (define (visit-cont cont)
- (match cont
- (($ $cont sym ($ $kargs names syms body))
- (visit-term body sym syms))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (when clause (visit-cont clause)))
- (($ $cont sym ($ $kclause arity body alternate))
- (visit-cont body)
- (when alternate (visit-cont alternate)))
- (($ $cont sym _) #f)))
- (define (visit-term term term-k term-args)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body term-k term-args))
- (($ $continue k src ($ $values args))
- (when (and (equal? term-args args) (not (eq? k term-k)))
- (hashq-set! table term-k k)))
- (($ $continue k src (and fun ($ $fun)))
- (visit-fun fun))
- (($ $continue k src ($ $rec names syms funs))
- (for-each visit-fun funs))
- (($ $continue k src _)
- #f)))
- (define (visit-fun fun)
- (match fun
- (($ $fun body)
- (visit-cont body))))
- (visit-cont fun)
- table))
-
-(define (eta-reduce fun)
- (let ((table (compute-eta-reductions fun))
- (dfg (compute-dfg fun)))
- (define (reduce* k scope values?)
- (match (hashq-ref table k)
- (#f k)
- (k*
- (if (and (continuation-bound-in? k* scope dfg)
- (or values?
- (match (lookup-cont k* dfg)
- (($ $kargs) #t)
- (_ #f))))
- (reduce* k* scope values?)
- k))))
- (define (reduce k scope)
- (reduce* k scope #f))
- (define (reduce-values k scope)
- (reduce* k scope #t))
- (define (reduce-const k src scope const)
- (let lp ((k k) (seen '()) (const const))
- (match (lookup-cont k dfg)
- (($ $kargs (_) (arg) term)
- (match (find-call term)
- (($ $continue k* src* ($ $values (arg*)))
- (and (eqv? arg arg*)
- (not (memq k* seen))
- (lp k* (cons k seen) const)))
- (($ $continue k* src* ($ $primcall 'not (arg*)))
- (and (eqv? arg arg*)
- (not (memq k* seen))
- (lp k* (cons k seen) (not const))))
- (($ $continue k* src* ($ $branch kt ($ $values (arg*))))
- (and (eqv? arg arg*)
- (let ((k* (if const kt k*)))
- (and (continuation-bound-in? k* scope dfg)
- (build-cps-term
- ($continue k* src ($values ())))))))
- (_
- (and (continuation-bound-in? k scope dfg)
- (build-cps-term
- ($continue k src ($const const)))))))
- (_ #f))))
- (define (visit-cont cont scope)
- (rewrite-cps-cont cont
- (($ $cont sym ($ $kargs names syms body))
- (sym ($kargs names syms ,(visit-term body sym))))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (sym ($kfun src meta self ,tail
- ,(and clause (visit-cont clause sym)))))
- (($ $cont sym ($ $kclause arity body alternate))
- (sym ($kclause ,arity ,(visit-cont body sym)
- ,(and alternate (visit-cont alternate sym)))))
- (($ $cont sym ($ $kreceive ($ $arity req () rest () #f) kargs))
- (sym ($kreceive req rest (reduce kargs scope))))))
- (define (visit-term term scope)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map (cut visit-cont <> scope) conts)
- ,(visit-term body scope)))
- (($ $continue k src ($ $values args))
- ($continue (reduce-values k scope) src ($values args)))
- (($ $continue k src (and fun ($ $fun)))
- ($continue (reduce k scope) src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names syms funs))
- ($continue k src ($rec names syms (map visit-fun funs))))
- (($ $continue k src ($ $const const))
- ,(let ((k (reduce k scope)))
- (or (reduce-const k src scope const)
- (build-cps-term ($continue k src ($const const))))))
- (($ $continue k src exp)
- ($continue (reduce k scope) src ,exp))))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(visit-cont body #f)))))
- (visit-cont fun #f)))
-
-(define (compute-beta-reductions fun)
- ;; A continuation's body can be inlined in place of a $values
- ;; expression if the continuation is a $kargs. It should only be
- ;; inlined if it is used only once, and not recursively.
- (let ((var-table (make-hash-table))
- (k-table (make-hash-table))
- (dfg (compute-dfg fun)))
- (define (visit-cont cont)
- (match cont
- (($ $cont sym ($ $kargs names syms body))
- (visit-term body))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (when clause (visit-cont clause)))
- (($ $cont sym ($ $kclause arity body alternate))
- (visit-cont body)
- (when alternate (visit-cont alternate)))
- (($ $cont sym (or ($ $ktail) ($ $kreceive)))
- #f)))
- (define (visit-term term)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body))
- (($ $continue k src ($ $values args))
- (match (lookup-cont k dfg)
- (($ $kargs names syms body)
- (match (lookup-predecessors k dfg)
- ((_)
- ;; There is only one use, and it is this use. We assume
- ;; it's not recursive, as there would to be some other
- ;; use for control flow to reach this loop. Store the k
- ;; -> body mapping in the table. Also store the
- ;; substitutions for the variables bound by the inlined
- ;; continuation.
- (for-each (cut hashq-set! var-table <> <>) syms args)
- (hashq-set! k-table k body))
- (_ #f)))
- (_ #f)))
- (($ $continue k src (and fun ($ $fun)))
- (visit-fun fun))
- (($ $continue k src ($ $rec names syms funs))
- (for-each visit-fun funs))
- (($ $continue k src _)
- #f)))
- (define (visit-fun fun)
- (match fun
- (($ $fun body)
- (visit-cont body))))
- (visit-cont fun)
- (values var-table k-table)))
-
-(define (beta-reduce fun)
- (let-values (((var-table k-table) (compute-beta-reductions fun)))
- (define (subst var)
- (cond ((hashq-ref var-table var) => subst)
- (else var)))
- (define (must-visit-cont cont)
- (or (visit-cont cont)
- (error "continuation must not be inlined" cont)))
- (define (visit-cont cont)
- (match cont
- (($ $cont sym cont)
- (and (not (hashq-ref k-table sym))
- (rewrite-cps-cont cont
- (($ $kargs names syms body)
- (sym ($kargs names syms ,(visit-term body))))
- (($ $kfun src meta self tail clause)
- (sym ($kfun src meta self ,tail
- ,(and clause (must-visit-cont clause)))))
- (($ $kclause arity body alternate)
- (sym ($kclause ,arity ,(must-visit-cont body)
- ,(and alternate (must-visit-cont alternate)))))
- (($ $kreceive)
- (sym ,cont)))))))
- (define (visit-term term)
- (match term
- (($ $letk conts body)
- (match (filter-map visit-cont conts)
- (() (visit-term body))
- (conts (build-cps-term
- ($letk ,conts ,(visit-term body))))))
- (($ $continue k src exp)
- (cond
- ((hashq-ref k-table k) => visit-term)
- (else
- (build-cps-term ($continue k src ,(visit-exp exp))))))))
- (define (visit-exp exp)
- (match exp
- ((or ($ $const) ($ $prim)) exp)
- (($ $fun) (visit-fun exp))
- (($ $rec names syms funs)
- (build-cps-exp ($rec names (map subst syms) (map visit-fun funs))))
- (($ $call proc args)
- (let ((args (map subst args)))
- (build-cps-exp ($call (subst proc) args))))
- (($ $callk k proc args)
- (let ((args (map subst args)))
- (build-cps-exp ($callk k (subst proc) args))))
- (($ $primcall name args)
- (let ((args (map subst args)))
- (build-cps-exp ($primcall name args))))
- (($ $values args)
- (let ((args (map subst args)))
- (build-cps-exp ($values args))))
- (($ $branch kt exp)
- (build-cps-exp ($branch kt ,(visit-exp exp))))
- (($ $prompt escape? tag handler)
- (build-cps-exp ($prompt escape? (subst tag) handler)))))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(must-visit-cont body)))))
- (must-visit-cont fun)))
-
-;; Rewrite the scope tree to reflect the dominator tree. Precondition:
-;; the fun has been renumbered, its min-label is 0, and its labels are
-;; packed.
-(define (redominate fun)
- (let* ((dfg (compute-dfg fun))
- (idoms (compute-idoms dfg 0 (dfg-label-count dfg)))
- (doms (compute-dom-edges idoms 0)))
- (define (visit-fun-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail
- ,(and clause (visit-fun-cont clause)))))
- (($ $cont label ($ $kclause arity ($ $cont kbody body) alternate))
- (label ($kclause ,arity ,(visit-cont kbody body)
- ,(and alternate (visit-fun-cont alternate)))))))
-
- (define (visit-cont label cont)
- (rewrite-cps-cont cont
- (($ $kargs names vars body)
- (label ($kargs names vars ,(visit-term body label))))
- (_ (label ,cont))))
-
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(visit-fun-cont body)))))
-
- (define (visit-exp k src exp)
- (rewrite-cps-term exp
- (($ $fun body)
- ($continue k src ,(visit-fun exp)))
- (($ $rec names syms funs)
- ($continue k src ($rec names syms (map visit-fun funs))))
- (_
- ($continue k src ,exp))))
-
- (define (visit-term term label)
- (define (visit-dom-conts label)
- (let ((cont (lookup-cont label dfg)))
- (match cont
- (($ $ktail) '())
- (($ $kargs) (list (visit-cont label cont)))
- (else
- (cons (visit-cont label cont)
- (visit-dom-conts* (vector-ref doms label)))))))
-
- (define (visit-dom-conts* labels)
- (match labels
- (() '())
- ((label . labels)
- (append (visit-dom-conts label)
- (visit-dom-conts* labels)))))
-
- (rewrite-cps-term term
- (($ $letk conts body)
- ,(visit-term body label))
- (($ $continue k src exp)
- ,(let ((conts (visit-dom-conts* (vector-ref doms label))))
- (if (null? conts)
- (visit-exp k src exp)
- (build-cps-term
- ($letk ,conts ,(visit-exp k src exp))))))))
-
- (visit-fun-cont fun)))
-
-(define (simplify fun)
- ;; Renumbering prunes continuations that are made unreachable by
- ;; eta/beta reductions.
- (redominate (renumber (eta-reduce (beta-reduce fun)))))
diff --git a/module/language/cps/specialize-primcalls.scm b/module/language/cps/specialize-primcalls.scm
deleted file mode 100644
index e5b76fb13..000000000
--- a/module/language/cps/specialize-primcalls.scm
+++ /dev/null
@@ -1,107 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Some bytecode operations can encode an immediate as an operand.
-;;; This pass tranforms generic primcalls to these specialized
-;;; primcalls, if possible.
-;;;
-;;; Code:
-
-(define-module (language cps specialize-primcalls)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:export (specialize-primcalls))
-
-(define (specialize-primcalls fun)
- (let ((dfg (compute-dfg fun #:global? #t)))
- (with-fresh-name-state-from-dfg dfg
- (define (immediate-u8? sym)
- (call-with-values (lambda () (find-constant-value sym dfg))
- (lambda (has-const? val)
- (and has-const? (integer? val) (exact? val) (<= 0 val 255)))))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont sym ($ $kargs names syms body))
- (sym ($kargs names syms ,(visit-term body))))
- (($ $cont sym ($ $kfun src meta self tail clause))
- (sym ($kfun src meta self ,tail
- ,(and clause (visit-cont clause)))))
- (($ $cont sym ($ $kclause arity body alternate))
- (sym ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (($ $cont)
- ,cont)))
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts)
- ,(visit-term body)))
- (($ $continue k src (and fun ($ $fun)))
- ($continue k src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names syms funs))
- ($continue k src ($rec names syms (map visit-fun funs))))
- (($ $continue k src ($ $primcall name args))
- ,(visit-primcall k src name args))
- (($ $continue)
- ,term)))
- (define (visit-primcall k src name args)
- ;; If we introduce a VM op from a primcall without a VM op, we
- ;; will need to ensure that the return arity matches. Rely on the
- ;; elide-values pass to clean up.
- (define-syntax-rule (adapt-void exp)
- (let-fresh (k* kvoid) (val)
- (build-cps-term
- ($letk ((k* ($kargs ('val) (val)
- ($continue k src ($primcall 'values (val)))))
- (kvoid ($kargs () ()
- ($continue k* src ($const *unspecified*)))))
- ($continue kvoid src exp)))))
- (define-syntax-rule (adapt-val exp)
- (let-fresh (k*) (val)
- (build-cps-term
- ($letk ((k* ($kargs ('val) (val)
- ($continue k src ($primcall 'values (val))))))
- ($continue k* src exp)))))
- (match (cons name args)
- (('make-vector (? immediate-u8? n) init)
- (adapt-val ($primcall 'make-vector/immediate (n init))))
- (('vector-ref v (? immediate-u8? n))
- (build-cps-term
- ($continue k src ($primcall 'vector-ref/immediate (v n)))))
- (('vector-set! v (? immediate-u8? n) x)
- (build-cps-term
- ($continue k src ($primcall 'vector-set!/immediate (v n x)))))
- (('allocate-struct v (? immediate-u8? n))
- (adapt-val ($primcall 'allocate-struct/immediate (v n))))
- (('struct-ref s (? immediate-u8? n))
- (adapt-val ($primcall 'struct-ref/immediate (s n))))
- (('struct-set! s (? immediate-u8? n) x)
- (build-cps-term
- ($continue k src ($primcall 'struct-set!/immediate (s n x)))))
- (_
- (build-cps-term ($continue k src ($primcall name args))))))
-
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(visit-cont body)))))
-
- (visit-cont fun))))
diff --git a/module/language/cps/type-fold.scm b/module/language/cps/type-fold.scm
deleted file mode 100644
index ba66ec3ff..000000000
--- a/module/language/cps/type-fold.scm
+++ /dev/null
@@ -1,443 +0,0 @@
-;;; Abstract constant folding on CPS
-;;; Copyright (C) 2014, 2015 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 program. If not, see
-;;; .
-
-;;; Commentary:
-;;;
-;;; This pass uses the abstract interpretation provided by type analysis
-;;; to fold constant values and type predicates. It is most profitably
-;;; run after CSE, to take advantage of scalar replacement.
-;;;
-;;; Code:
-
-(define-module (language cps type-fold)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps renumber)
- #:use-module (language cps types)
- #:use-module (system base target)
- #:export (type-fold))
-
-
-
-
-;; Branch folders.
-
-(define &scalar-types
- (logior &exact-integer &flonum &char &unspecified &false &true &nil &null))
-
-(define *branch-folders* (make-hash-table))
-
-(define-syntax-rule (define-branch-folder name f)
- (hashq-set! *branch-folders* 'name f))
-
-(define-syntax-rule (define-branch-folder-alias to from)
- (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from)))
-
-(define-syntax-rule (define-unary-branch-folder (name arg min max) body ...)
- (define-branch-folder name (lambda (arg min max) body ...)))
-
-(define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0
- arg1 min1 max1)
- body ...)
- (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...)))
-
-(define-syntax-rule (define-unary-type-predicate-folder name &type)
- (define-unary-branch-folder (name type min max)
- (let ((type* (logand type &type)))
- (cond
- ((zero? type*) (values #t #f))
- ((eqv? type type*) (values #t #t))
- (else (values #f #f))))))
-
-;; All the cases that are in compile-bytecode.
-(define-unary-type-predicate-folder pair? &pair)
-(define-unary-type-predicate-folder null? &null)
-(define-unary-type-predicate-folder nil? &nil)
-(define-unary-type-predicate-folder symbol? &symbol)
-(define-unary-type-predicate-folder variable? &box)
-(define-unary-type-predicate-folder vector? &vector)
-(define-unary-type-predicate-folder struct? &struct)
-(define-unary-type-predicate-folder string? &string)
-(define-unary-type-predicate-folder number? &number)
-(define-unary-type-predicate-folder char? &char)
-
-(define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1)
- (cond
- ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0))
- (values #t #f))
- ((and (eqv? type0 type1)
- (eqv? min0 min1 max0 max1)
- (zero? (logand type0 (1- type0)))
- (not (zero? (logand type0 &scalar-types))))
- (values #t #t))
- (else
- (values #f #f))))
-(define-branch-folder-alias eqv? eq?)
-(define-branch-folder-alias equal? eq?)
-
-(define (compare-ranges type0 min0 max0 type1 min1 max1)
- (and (zero? (logand (logior type0 type1) (lognot &real)))
- (cond ((< max0 min1) '<)
- ((> min0 max1) '>)
- ((= min0 max0 min1 max1) '=)
- ((<= max0 min1) '<=)
- ((>= min0 max1) '>=)
- (else #f))))
-
-(define-binary-branch-folder (< type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((<) (values #t #t))
- ((= >= >) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((< <= =) (values #t #t))
- ((>) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((=) (values #t #t))
- ((< >) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((> >= =) (values #t #t))
- ((<) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (> type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((>) (values #t #t))
- ((= <= <) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (logtest type0 min0 max0 type1 min1 max1)
- (define (logand-min a b)
- (if (< a b 0)
- (min a b)
- 0))
- (define (logand-max a b)
- (if (< a b 0)
- 0
- (max a b)))
- (if (and (= min0 max0) (= min1 max1) (eqv? type0 type1 &exact-integer))
- (values #t (logtest min0 min1))
- (values #f #f)))
-
-
-
-
-;; Strength reduction.
-
-(define *primcall-reducers* (make-hash-table))
-
-(define-syntax-rule (define-primcall-reducer name f)
- (hashq-set! *primcall-reducers* 'name f))
-
-(define-syntax-rule (define-unary-primcall-reducer (name dfg k src
- arg type min max)
- body ...)
- (define-primcall-reducer name
- (lambda (dfg k src arg type min max) body ...)))
-
-(define-syntax-rule (define-binary-primcall-reducer (name dfg k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- body ...)
- (define-primcall-reducer name
- (lambda (dfg k src arg0 type0 min0 max0 arg1 type1 min1 max1) body ...)))
-
-(define-binary-primcall-reducer (mul dfg k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- (define (negate arg)
- (let-fresh (kzero) (zero)
- (build-cps-term
- ($letk ((kzero ($kargs (#f) (zero)
- ($continue k src ($primcall 'sub (zero arg))))))
- ($continue kzero src ($const 0))))))
- (define (zero)
- (build-cps-term ($continue k src ($const 0))))
- (define (identity arg)
- (build-cps-term ($continue k src ($values (arg)))))
- (define (double arg)
- (build-cps-term ($continue k src ($primcall 'add (arg arg)))))
- (define (power-of-two constant arg)
- (let ((n (let lp ((bits 0) (constant constant))
- (if (= constant 1) bits (lp (1+ bits) (ash constant -1))))))
- (let-fresh (kbits) (bits)
- (build-cps-term
- ($letk ((kbits ($kargs (#f) (bits)
- ($continue k src ($primcall 'ash (arg bits))))))
- ($continue kbits src ($const n)))))))
- (define (mul/constant constant constant-type arg arg-type)
- (and (or (= constant-type &exact-integer) (= constant-type arg-type))
- (case constant
- ;; (* arg -1) -> (- 0 arg)
- ((-1) (negate arg))
- ;; (* arg 0) -> 0 if arg is not a flonum or complex
- ((0) (and (= constant-type &exact-integer)
- (zero? (logand arg-type
- (lognot (logior &flonum &complex))))
- (zero)))
- ;; (* arg 1) -> arg
- ((1) (identity arg))
- ;; (* arg 2) -> (+ arg arg)
- ((2) (double arg))
- (else (and (= constant-type arg-type &exact-integer)
- (positive? constant)
- (zero? (logand constant (1- constant)))
- (power-of-two constant arg))))))
- (cond
- ((logtest (logior type0 type1) (lognot &number)) #f)
- ((= min0 max0) (mul/constant min0 type0 arg1 type1))
- ((= min1 max1) (mul/constant min1 type1 arg0 type0))
- (else #f)))
-
-(define-binary-primcall-reducer (logbit? dfg k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- (define (convert-to-logtest bool-term)
- (let-fresh (kt kf kmask kbool) (mask bool)
- (build-cps-term
- ($letk ((kt ($kargs () ()
- ($continue kbool src ($const #t))))
- (kf ($kargs () ()
- ($continue kbool src ($const #f))))
- (kbool ($kargs (#f) (bool)
- ,(bool-term bool)))
- (kmask ($kargs (#f) (mask)
- ($continue kf src
- ($branch kt ($primcall 'logtest (mask arg1)))))))
- ,(if (eq? min0 max0)
- ($continue kmask src ($const (ash 1 min0)))
- (let-fresh (kone) (one)
- (build-cps-term
- ($letk ((kone ($kargs (#f) (one)
- ($continue kmask src
- ($primcall 'ash (one arg0))))))
- ($continue kone src ($const 1))))))))))
- ;; Hairiness because we are converting from a primcall with unknown
- ;; arity to a branching primcall.
- (let ((positive-fixnum-bits (- (* (target-word-size) 8) 3)))
- (and (= type0 &exact-integer)
- (<= 0 min0 positive-fixnum-bits)
- (<= 0 max0 positive-fixnum-bits)
- (match (lookup-cont k dfg)
- (($ $kreceive arity kargs)
- (match arity
- (($ $arity (_) () (not #f) () #f)
- (convert-to-logtest
- (lambda (bool)
- (let-fresh (knil) (nil)
- (build-cps-term
- ($letk ((knil ($kargs (#f) (nil)
- ($continue kargs src
- ($values (bool nil))))))
- ($continue knil src ($const '()))))))))
- (_
- (convert-to-logtest
- (lambda (bool)
- (build-cps-term
- ($continue k src ($primcall 'values (bool)))))))))
- (($ $ktail)
- (convert-to-logtest
- (lambda (bool)
- (build-cps-term
- ($continue k src ($primcall 'return (bool)))))))))))
-
-
-
-
-;;
-
-(define (fold-and-reduce fun dfg min-label min-var)
- (define (scalar-value type val)
- (cond
- ((eqv? type &exact-integer) val)
- ((eqv? type &flonum) (exact->inexact val))
- ((eqv? type &char) (integer->char val))
- ((eqv? type &unspecified) *unspecified*)
- ((eqv? type &false) #f)
- ((eqv? type &true) #t)
- ((eqv? type &nil) #nil)
- ((eqv? type &null) '())
- (else (error "unhandled type" type val))))
- (let* ((typev (infer-types fun dfg))
- (label-count ((make-local-cont-folder label-count)
- (lambda (k cont label-count) (1+ label-count))
- fun 0))
- (folded? (make-bitvector label-count #f))
- (folded-values (make-vector label-count #f))
- (reduced-terms (make-vector label-count #f)))
- (define (label->idx label) (- label min-label))
- (define (var->idx var) (- var min-var))
- (define (maybe-reduce-primcall! label k src name args)
- (let* ((reducer (hashq-ref *primcall-reducers* name)))
- (when reducer
- (vector-set!
- reduced-terms
- (label->idx label)
- (match args
- ((arg0)
- (call-with-values (lambda () (lookup-pre-type typev label arg0))
- (lambda (type0 min0 max0)
- (reducer dfg k src arg0 type0 min0 max0))))
- ((arg0 arg1)
- (call-with-values (lambda () (lookup-pre-type typev label arg0))
- (lambda (type0 min0 max0)
- (call-with-values (lambda () (lookup-pre-type typev label arg1))
- (lambda (type1 min1 max1)
- (reducer dfg k src arg0 type0 min0 max0
- arg1 type1 min1 max1))))))
- (_ #f))))))
- (define (maybe-fold-value! label name def)
- (call-with-values (lambda () (lookup-post-type typev label def 0))
- (lambda (type min max)
- (cond
- ((and (not (zero? type))
- (zero? (logand type (1- type)))
- (zero? (logand type (lognot &scalar-types)))
- (eqv? min max))
- (bitvector-set! folded? (label->idx label) #t)
- (vector-set! folded-values (label->idx label)
- (scalar-value type min))
- #t)
- (else #f)))))
- (define (maybe-fold-unary-branch! label name arg)
- (let* ((folder (hashq-ref *branch-folders* name)))
- (when folder
- (call-with-values (lambda () (lookup-pre-type typev label arg))
- (lambda (type min max)
- (call-with-values (lambda () (folder type min max))
- (lambda (f? v)
- (bitvector-set! folded? (label->idx label) f?)
- (vector-set! folded-values (label->idx label) v))))))))
- (define (maybe-fold-binary-branch! label name arg0 arg1)
- (let* ((folder (hashq-ref *branch-folders* name)))
- (when folder
- (call-with-values (lambda () (lookup-pre-type typev label arg0))
- (lambda (type0 min0 max0)
- (call-with-values (lambda () (lookup-pre-type typev label arg1))
- (lambda (type1 min1 max1)
- (call-with-values (lambda ()
- (folder type0 min0 max0 type1 min1 max1))
- (lambda (f? v)
- (bitvector-set! folded? (label->idx label) f?)
- (vector-set! folded-values (label->idx label) v))))))))))
- (define (visit-cont cont)
- (match cont
- (($ $cont label ($ $kargs _ _ body))
- (visit-term body label))
- (($ $cont label ($ $kclause arity body alternate))
- (visit-cont body)
- (visit-cont alternate))
- (_ #f)))
- (define (visit-term term label)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body label))
- (($ $continue k src ($ $primcall name args))
- ;; We might be able to fold primcalls that define a value.
- (match (lookup-cont k dfg)
- (($ $kargs (_) (def))
- ;(pk 'maybe-fold-value src name args)
- (unless (maybe-fold-value! label name def)
- (maybe-reduce-primcall! label k src name args)))
- (_
- (maybe-reduce-primcall! label k src name args))))
- (($ $continue kf src ($ $branch kt ($ $primcall name args)))
- ;; We might be able to fold primcalls that branch.
- ;(pk 'maybe-fold-branch label src name args)
- (match args
- ((arg)
- (maybe-fold-unary-branch! label name arg))
- ((arg0 arg1)
- (maybe-fold-binary-branch! label name arg0 arg1))))
- (_ #f)))
- (when typev
- (match fun
- (($ $cont kfun ($ $kfun src meta self tail clause))
- (visit-cont clause))))
- (values folded? folded-values reduced-terms)))
-
-(define (fold-constants* fun dfg)
- (match fun
- (($ $cont min-label ($ $kfun _ _ min-var))
- (call-with-values (lambda () (fold-and-reduce fun dfg min-label min-var))
- (lambda (folded? folded-values reduced-terms)
- (define (label->idx label) (- label min-label))
- (define (var->idx var) (- var min-var))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs names syms body))
- (label ($kargs names syms ,(visit-term body label))))
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-cont alternate)))))
- (_ ,cont)))
- (define (visit-term term label)
- (rewrite-cps-term term
- (($ $letk conts body)
- ($letk ,(map visit-cont conts)
- ,(visit-term body label)))
- (($ $continue k src (and fun ($ $fun)))
- ($continue k src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names vars funs))
- ($continue k src ($rec names vars (map visit-fun funs))))
- (($ $continue k src (and primcall ($ $primcall name args)))
- ,(cond
- ((bitvector-ref folded? (label->idx label))
- (let ((val (vector-ref folded-values (label->idx label))))
- ;; Uncomment for debugging.
- ;; (pk 'folded src primcall val)
- (let-fresh (k*) (v*)
- ;; Rely on DCE to elide this expression, if
- ;; possible.
- (build-cps-term
- ($letk ((k* ($kargs (#f) (v*)
- ($continue k src ($const val)))))
- ($continue k* src ,primcall))))))
- (else
- (or (vector-ref reduced-terms (label->idx label))
- term))))
- (($ $continue kf src ($ $branch kt ($ $primcall)))
- ,(if (bitvector-ref folded? (label->idx label))
- ;; Folded branch.
- (let ((val (vector-ref folded-values (label->idx label))))
- (build-cps-term
- ($continue (if val kt kf) src ($values ()))))
- term))
- (_ ,term)))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(fold-constants* body dfg)))))
- (rewrite-cps-cont fun
- (($ $cont kfun ($ $kfun src meta self tail clause))
- (kfun ($kfun src meta self ,tail ,(visit-cont clause))))))))))
-
-(define (type-fold fun)
- (let* ((fun (renumber fun))
- (dfg (compute-dfg fun)))
- (with-fresh-name-state-from-dfg dfg
- (fold-constants* fun dfg))))
diff --git a/module/language/cps/types.scm b/module/language/cps/types.scm
deleted file mode 100644
index 5e0b2d083..000000000
--- a/module/language/cps/types.scm
+++ /dev/null
@@ -1,1424 +0,0 @@
-;;; Type analysis on CPS
-;;; Copyright (C) 2014, 2015 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 program. If not, see
-;;; .
-
-;;; Commentary:
-;;;
-;;; Type analysis computes the possible types and ranges that values may
-;;; have at all program positions. This analysis can help to prove that
-;;; a primcall has no side-effects, if its arguments have the
-;;; appropriate type and range. It can also enable constant folding of
-;;; type predicates and, in the future, enable the compiler to choose
-;;; untagged, unboxed representations for numbers.
-;;;
-;;; For the purposes of this analysis, a "type" is an aspect of a value
-;;; that will not change. Guile's CPS intermediate language does not
-;;; carry manifest type information that asserts properties about given
-;;; values; instead, we recover this information via flow analysis,
-;;; garnering properties from type predicates, constant literals,
-;;; primcall results, and primcalls that assert that their arguments are
-;;; of particular types.
-;;;
-;;; A range denotes a subset of the set of values in a type, bounded by
-;;; a minimum and a maximum. The precise meaning of a range depends on
-;;; the type. For real numbers, the range indicates an inclusive lower
-;;; and upper bound on the integer value of a type. For vectors, the
-;;; range indicates the length of the vector. The range is limited to a
-;;; signed 32-bit value, with the smallest and largest values indicating
-;;; -inf.0 and +inf.0, respectively. For some types, like pairs, the
-;;; concept of "range" makes no sense. In these cases we consider the
-;;; range to be -inf.0 to +inf.0.
-;;;
-;;; Types are represented as a bitfield. Fewer bits means a more precise
-;;; type. Although normally only values that have a single type will
-;;; have an associated range, this is not enforced. The range applies
-;;; to all types in the bitfield. When control flow meets, the types and
-;;; ranges meet with the union operator.
-;;;
-;;; It is not practical to precisely compute value ranges in all cases.
-;;; For example, in the following case:
-;;;
-;;; (let lp ((n 0)) (when (foo) (lp (1+ n))))
-;;;
-;;; The first time that range analysis visits the program, N is
-;;; determined to be the exact integer 0. The second time, it is an
-;;; exact integer in the range [0, 1]; the third, [0, 2]; and so on.
-;;; This analysis will terminate, but only after the positive half of
-;;; the 32-bit range has been fully explored and we decide that the
-;;; range of N is [0, +inf.0]. At the same time, we want to do range
-;;; analysis and type analysis at the same time, as there are
-;;; interactions between them, notably in the case of `sqrt' which
-;;; returns a complex number if its argument cannot be proven to be
-;;; non-negative. So what we do is, once the types reach a fixed point,
-;;; we cause control-flow joins that would expand the range of a value
-;;; to saturate that range towards positive or infinity (as
-;;; appropriate).
-;;;
-;;; A naive approach to type analysis would build up a table that has
-;;; entries for all variables at all program points, but this has
-;;; N-squared complexity and quickly grows unmanageable. Instead, we
-;;; use _intmaps_ from (language cps intmap) to share state between
-;;; connected program points.
-;;;
-;;; Code:
-
-(define-module (language cps types)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps intmap)
- #:use-module (rnrs bytevectors)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-11)
- #:export (;; Specific types.
- &exact-integer
- &flonum
- &complex
- &fraction
-
- &char
- &unspecified
- &unbound
- &false
- &true
- &nil
- &null
- &symbol
- &keyword
-
- &procedure
-
- &pointer
- &fluid
- &pair
- &vector
- &box
- &struct
- &string
- &bytevector
- &bitvector
- &array
- &hash-table
-
- ;; Union types.
- &number &real
-
- infer-types
- lookup-pre-type
- lookup-post-type
- primcall-types-check?))
-
-(define-syntax define-flags
- (lambda (x)
- (syntax-case x ()
- ((_ all shift name ...)
- (let ((count (length #'(name ...))))
- (with-syntax (((n ...) (iota count))
- (count count))
- #'(begin
- (define-syntax name (identifier-syntax (ash 1 n)))
- ...
- (define-syntax all (identifier-syntax (1- (ash 1 count))))
- (define-syntax shift (identifier-syntax count)))))))))
-
-;; More precise types have fewer bits.
-(define-flags &all-types &type-bits
- &exact-integer
- &flonum
- &complex
- &fraction
-
- &char
- &unspecified
- &unbound
- &false
- &true
- &nil
- &null
- &symbol
- &keyword
-
- &procedure
-
- &pointer
- &fluid
- &pair
- &vector
- &box
- &struct
- &string
- &bytevector
- &bitvector
- &array
- &hash-table)
-
-(define-syntax &no-type (identifier-syntax 0))
-
-(define-syntax &number
- (identifier-syntax (logior &exact-integer &flonum &complex &fraction)))
-(define-syntax &real
- (identifier-syntax (logior &exact-integer &flonum &fraction)))
-
-(define-syntax *max-s32* (identifier-syntax (- (ash 1 31) 1)))
-(define-syntax *min-s32* (identifier-syntax (- 0 (ash 1 31))))
-
-;; Versions of min and max that do not coerce exact numbers to become
-;; inexact.
-(define min
- (case-lambda
- ((a b) (if (< a b) a b))
- ((a b c) (min (min a b) c))
- ((a b c d) (min (min a b) c d))))
-(define max
- (case-lambda
- ((a b) (if (> a b) a b))
- ((a b c) (max (max a b) c))
- ((a b c d) (max (max a b) c d))))
-
-
-
-(define-syntax-rule (define-compile-time-value name val)
- (define-syntax name
- (make-variable-transformer
- (lambda (x)
- (syntax-case x (set!)
- (var (identifier? #'var)
- (datum->syntax #'var val)))))))
-
-(define-compile-time-value min-fixnum most-negative-fixnum)
-(define-compile-time-value max-fixnum most-positive-fixnum)
-
-(define-inlinable (make-unclamped-type-entry type min max)
- (vector type min max))
-(define-inlinable (type-entry-type tentry)
- (vector-ref tentry 0))
-(define-inlinable (type-entry-clamped-min tentry)
- (vector-ref tentry 1))
-(define-inlinable (type-entry-clamped-max tentry)
- (vector-ref tentry 2))
-
-(define-syntax-rule (clamp-range val)
- (cond
- ((< val min-fixnum) min-fixnum)
- ((< max-fixnum val) max-fixnum)
- (else val)))
-
-(define-inlinable (make-type-entry type min max)
- (vector type (clamp-range min) (clamp-range max)))
-(define-inlinable (type-entry-min tentry)
- (let ((min (type-entry-clamped-min tentry)))
- (if (eq? min min-fixnum) -inf.0 min)))
-(define-inlinable (type-entry-max tentry)
- (let ((max (type-entry-clamped-max tentry)))
- (if (eq? max max-fixnum) +inf.0 max)))
-
-(define all-types-entry (make-type-entry &all-types -inf.0 +inf.0))
-
-(define* (var-type-entry typeset var #:optional (default all-types-entry))
- (intmap-ref typeset var (lambda (_) default)))
-
-(define (var-type typeset var)
- (type-entry-type (var-type-entry typeset var)))
-(define (var-min typeset var)
- (type-entry-min (var-type-entry typeset var)))
-(define (var-max typeset var)
- (type-entry-max (var-type-entry typeset var)))
-
-;; Is the type entry A contained entirely within B?
-(define (type-entry<=? a b)
- (match (cons a b)
- ((#(a-type a-min a-max) . #(b-type b-min b-max))
- (and (eqv? b-type (logior a-type b-type))
- (<= b-min a-min)
- (>= b-max a-max)))))
-
-(define (type-entry-union a b)
- (cond
- ((type-entry<=? b a) a)
- ((type-entry<=? a b) b)
- (else (make-type-entry
- (logior (type-entry-type a) (type-entry-type b))
- (min (type-entry-clamped-min a) (type-entry-clamped-min b))
- (max (type-entry-clamped-max a) (type-entry-clamped-max b))))))
-
-(define (type-entry-intersection a b)
- (cond
- ((type-entry<=? a b) a)
- ((type-entry<=? b a) b)
- (else (make-type-entry
- (logand (type-entry-type a) (type-entry-type b))
- (max (type-entry-clamped-min a) (type-entry-clamped-min b))
- (min (type-entry-clamped-max a) (type-entry-clamped-max b))))))
-
-(define (adjoin-var typeset var entry)
- (intmap-add typeset var entry type-entry-union))
-
-(define (restrict-var typeset var entry)
- (intmap-add typeset var entry type-entry-intersection))
-
-(define (constant-type val)
- "Compute the type and range of VAL. Return three values: the type,
-minimum, and maximum."
- (define (return type val)
- (if val
- (make-type-entry type val val)
- (make-type-entry type -inf.0 +inf.0)))
- (cond
- ((number? val)
- (cond
- ((exact-integer? val) (return &exact-integer val))
- ((eqv? (imag-part val) 0)
- (if (nan? val)
- (make-type-entry &flonum -inf.0 +inf.0)
- (make-type-entry
- (if (exact? val) &fraction &flonum)
- (if (rational? val) (inexact->exact (floor val)) val)
- (if (rational? val) (inexact->exact (ceiling val)) val))))
- (else (return &complex #f))))
- ((eq? val '()) (return &null #f))
- ((eq? val #nil) (return &nil #f))
- ((eq? val #t) (return &true #f))
- ((eq? val #f) (return &false #f))
- ((char? val) (return &char (char->integer val)))
- ((eqv? val *unspecified*) (return &unspecified #f))
- ((symbol? val) (return &symbol #f))
- ((keyword? val) (return &keyword #f))
- ((pair? val) (return &pair #f))
- ((vector? val) (return &vector (vector-length val)))
- ((string? val) (return &string (string-length val)))
- ((bytevector? val) (return &bytevector (bytevector-length val)))
- ((bitvector? val) (return &bitvector (bitvector-length val)))
- ((array? val) (return &array (array-rank val)))
- ((not (variable-bound? (make-variable val))) (return &unbound #f))
-
- (else (error "unhandled constant" val))))
-
-(define *type-checkers* (make-hash-table))
-(define *type-inferrers* (make-hash-table))
-
-(define-syntax-rule (define-type-helper name)
- (define-syntax-parameter name
- (lambda (stx)
- (syntax-violation 'name
- "macro used outside of define-type"
- stx))))
-(define-type-helper define!)
-(define-type-helper restrict!)
-(define-type-helper &type)
-(define-type-helper &min)
-(define-type-helper &max)
-
-(define-syntax-rule (define-type-checker (name arg ...) body ...)
- (hashq-set!
- *type-checkers*
- 'name
- (lambda (typeset arg ...)
- (syntax-parameterize
- ((&type (syntax-rules () ((_ val) (var-type typeset val))))
- (&min (syntax-rules () ((_ val) (var-min typeset val))))
- (&max (syntax-rules () ((_ val) (var-max typeset val)))))
- body ...))))
-
-(define-syntax-rule (check-type arg type min max)
- ;; If the arg is negative, it is a closure variable.
- (and (>= arg 0)
- (zero? (logand (lognot type) (&type arg)))
- (<= min (&min arg))
- (<= (&max arg) max)))
-
-(define-syntax-rule (define-type-inferrer* (name succ var ...) body ...)
- (hashq-set!
- *type-inferrers*
- 'name
- (lambda (in succ var ...)
- (let ((out in))
- (syntax-parameterize
- ((define!
- (syntax-rules ()
- ((_ val type min max)
- (set! out (adjoin-var out val
- (make-type-entry type min max))))))
- (restrict!
- (syntax-rules ()
- ((_ val type min max)
- (set! out (restrict-var out val
- (make-type-entry type min max))))))
- (&type (syntax-rules () ((_ val) (var-type in val))))
- (&min (syntax-rules () ((_ val) (var-min in val))))
- (&max (syntax-rules () ((_ val) (var-max in val)))))
- body ...
- out)))))
-
-(define-syntax-rule (define-type-inferrer (name arg ...) body ...)
- (define-type-inferrer* (name succ arg ...) body ...))
-
-(define-syntax-rule (define-predicate-inferrer (name arg ... true?) body ...)
- (define-type-inferrer* (name succ arg ...)
- (let ((true? (not (zero? succ))))
- body ...)))
-
-(define-syntax define-simple-type-checker
- (lambda (x)
- (define (parse-spec l)
- (syntax-case l ()
- (() '())
- (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
- (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
- ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
- (syntax-case x ()
- ((_ (name arg-spec ...) result-spec ...)
- (with-syntax
- (((arg ...) (generate-temporaries #'(arg-spec ...)))
- (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...))))
- #'(define-type-checker (name arg ...)
- (and (check-type arg arg-type arg-min arg-max)
- ...)))))))
-
-(define-syntax define-simple-type-inferrer
- (lambda (x)
- (define (parse-spec l)
- (syntax-case l ()
- (() '())
- (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
- (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
- ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
- (syntax-case x ()
- ((_ (name arg-spec ...) result-spec ...)
- (with-syntax
- (((arg ...) (generate-temporaries #'(arg-spec ...)))
- (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...)))
- ((res ...) (generate-temporaries #'(result-spec ...)))
- (((res-type res-min res-max) ...) (parse-spec #'(result-spec ...))))
- #'(define-type-inferrer (name arg ... res ...)
- (restrict! arg arg-type arg-min arg-max)
- ...
- (define! res res-type res-min res-max)
- ...))))))
-
-(define-syntax-rule (define-simple-type (name arg-spec ...) result-spec ...)
- (begin
- (define-simple-type-checker (name arg-spec ...))
- (define-simple-type-inferrer (name arg-spec ...) result-spec ...)))
-
-(define-syntax-rule (define-simple-types
- ((name arg-spec ...) result-spec ...)
- ...)
- (begin
- (define-simple-type (name arg-spec ...) result-spec ...)
- ...))
-
-(define-syntax-rule (define-type-checker-aliases orig alias ...)
- (let ((check (hashq-ref *type-checkers* 'orig)))
- (hashq-set! *type-checkers* 'alias check)
- ...))
-(define-syntax-rule (define-type-inferrer-aliases orig alias ...)
- (let ((check (hashq-ref *type-inferrers* 'orig)))
- (hashq-set! *type-inferrers* 'alias check)
- ...))
-(define-syntax-rule (define-type-aliases orig alias ...)
- (begin
- (define-type-checker-aliases orig alias ...)
- (define-type-inferrer-aliases orig alias ...)))
-
-
-
-
-;;; This list of primcall type definitions follows the order of
-;;; effects-analysis.scm; please keep it in a similar order.
-;;;
-;;; There is no need to add checker definitions for expressions that do
-;;; not exhibit the &type-check effect, as callers should not ask if
-;;; such an expression does or does not type-check. For those that do
-;;; exhibit &type-check, you should define a type inferrer unless the
-;;; primcall will never typecheck.
-;;;
-;;; Likewise there is no need to define inferrers for primcalls which
-;;; return &all-types values and which never raise exceptions from which
-;;; we can infer the types of incoming values.
-
-
-
-
-;;;
-;;; Generic effect-free predicates.
-;;;
-
-(define-predicate-inferrer (eq? a b true?)
- ;; We can only propagate information down the true leg.
- (when true?
- (let ((type (logand (&type a) (&type b)))
- (min (max (&min a) (&min b)))
- (max (min (&max a) (&max b))))
- (restrict! a type min max)
- (restrict! b type min max))))
-(define-type-inferrer-aliases eq? eqv? equal?)
-
-(define-syntax-rule (define-simple-predicate-inferrer predicate type)
- (define-predicate-inferrer (predicate val true?)
- (let ((type (if true?
- type
- (logand (&type val) (lognot type)))))
- (restrict! val type -inf.0 +inf.0))))
-(define-simple-predicate-inferrer pair? &pair)
-(define-simple-predicate-inferrer null? &null)
-(define-simple-predicate-inferrer nil? &nil)
-(define-simple-predicate-inferrer symbol? &symbol)
-(define-simple-predicate-inferrer variable? &box)
-(define-simple-predicate-inferrer vector? &vector)
-(define-simple-predicate-inferrer struct? &struct)
-(define-simple-predicate-inferrer string? &string)
-(define-simple-predicate-inferrer bytevector? &bytevector)
-(define-simple-predicate-inferrer bitvector? &bitvector)
-(define-simple-predicate-inferrer keyword? &keyword)
-(define-simple-predicate-inferrer number? &number)
-(define-simple-predicate-inferrer char? &char)
-(define-simple-predicate-inferrer procedure? &procedure)
-(define-simple-predicate-inferrer thunk? &procedure)
-
-
-
-;;;
-;;; Fluids. Note that we can't track bound-ness of fluids, as pop-fluid
-;;; can change boundness.
-;;;
-
-(define-simple-types
- ((fluid-ref (&fluid 1)) &all-types)
- ((fluid-set! (&fluid 0 1) &all-types))
- ((push-fluid (&fluid 0 1) &all-types))
- ((pop-fluid)))
-
-
-
-
-;;;
-;;; Prompts. (Nothing to do.)
-;;;
-
-
-
-
-;;;
-;;; Pairs.
-;;;
-
-(define-simple-types
- ((cons &all-types &all-types) &pair)
- ((car &pair) &all-types)
- ((set-car! &pair &all-types))
- ((cdr &pair) &all-types)
- ((set-cdr! &pair &all-types)))
-
-
-
-
-;;;
-;;; Variables.
-;;;
-
-(define-simple-types
- ((box &all-types) (&box 1))
- ((box-ref (&box 1)) &all-types))
-
-(define-simple-type-checker (box-set! (&box 0 1) &all-types))
-(define-type-inferrer (box-set! box val)
- (restrict! box &box 1 1))
-
-
-
-
-;;;
-;;; Vectors.
-;;;
-
-;; This max-vector-len computation is a hack.
-(define *max-vector-len* (ash most-positive-fixnum -5))
-
-(define-simple-type-checker (make-vector (&exact-integer 0 *max-vector-len*)
- &all-types))
-(define-type-inferrer (make-vector size init result)
- (restrict! size &exact-integer 0 *max-vector-len*)
- (define! result &vector (max (&min size) 0) (&max size)))
-
-(define-type-checker (vector-ref v idx)
- (and (check-type v &vector 0 *max-vector-len*)
- (check-type idx &exact-integer 0 (1- (&min v)))))
-(define-type-inferrer (vector-ref v idx result)
- (restrict! v &vector (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max v)))
- (define! result &all-types -inf.0 +inf.0))
-
-(define-type-checker (vector-set! v idx val)
- (and (check-type v &vector 0 *max-vector-len*)
- (check-type idx &exact-integer 0 (1- (&min v)))))
-(define-type-inferrer (vector-set! v idx val)
- (restrict! v &vector (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max v))))
-
-(define-type-aliases make-vector make-vector/immediate)
-(define-type-aliases vector-ref vector-ref/immediate)
-(define-type-aliases vector-set! vector-set!/immediate)
-
-(define-simple-type-checker (vector-length &vector))
-(define-type-inferrer (vector-length v result)
- (restrict! v &vector 0 *max-vector-len*)
- (define! result &exact-integer (max (&min v) 0)
- (min (&max v) *max-vector-len*)))
-
-
-
-
-;;;
-;;; Structs.
-;;;
-
-;; No type-checker for allocate-struct, as we can't currently check that
-;; vt is actually a vtable.
-(define-type-inferrer (allocate-struct vt size result)
- (restrict! vt &struct vtable-offset-user +inf.0)
- (restrict! size &exact-integer 0 +inf.0)
- (define! result &struct (max (&min size) 0) (&max size)))
-
-(define-type-checker (struct-ref s idx)
- (and (check-type s &struct 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- ;; FIXME: is the field readable?
- (< (&max idx) (&min s))))
-(define-type-inferrer (struct-ref s idx result)
- (restrict! s &struct (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (define! result &all-types -inf.0 +inf.0))
-
-(define-type-checker (struct-set! s idx val)
- (and (check-type s &struct 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- ;; FIXME: is the field writable?
- (< (&max idx) (&min s))))
-(define-type-inferrer (struct-set! s idx val)
- (restrict! s &struct (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s))))
-
-(define-type-aliases allocate-struct allocate-struct/immediate)
-(define-type-aliases struct-ref struct-ref/immediate)
-(define-type-aliases struct-set! struct-set!/immediate)
-
-(define-simple-type (struct-vtable (&struct 0 +inf.0))
- (&struct vtable-offset-user +inf.0))
-
-
-
-
-;;;
-;;; Strings.
-;;;
-
-(define *max-char* (1- (ash 1 24)))
-
-(define-type-checker (string-ref s idx)
- (and (check-type s &string 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (< (&max idx) (&min s))))
-(define-type-inferrer (string-ref s idx result)
- (restrict! s &string (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (define! result &char 0 *max-char*))
-
-(define-type-checker (string-set! s idx val)
- (and (check-type s &string 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (check-type val &char 0 *max-char*)
- (< (&max idx) (&min s))))
-(define-type-inferrer (string-set! s idx val)
- (restrict! s &string (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (restrict! val &char 0 *max-char*))
-
-(define-simple-type-checker (string-length &string))
-(define-type-inferrer (string-length s result)
- (restrict! s &string 0 +inf.0)
- (define! result &exact-integer (max (&min s) 0) (&max s)))
-
-(define-simple-type (number->string &number) (&string 0 +inf.0))
-(define-simple-type (string->number (&string 0 +inf.0))
- ((logior &number &false) -inf.0 +inf.0))
-
-
-
-
-;;;
-;;; Bytevectors.
-;;;
-
-(define-simple-type-checker (bytevector-length &bytevector))
-(define-type-inferrer (bytevector-length bv result)
- (restrict! bv &bytevector 0 +inf.0)
- (define! result &exact-integer (max (&min bv) 0) (&max bv)))
-
-(define-syntax-rule (define-bytevector-accessors ref set type size min max)
- (begin
- (define-type-checker (ref bv idx)
- (and (check-type bv &bytevector 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (< (&max idx) (- (&min bv) size))))
- (define-type-inferrer (ref bv idx result)
- (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
- (restrict! idx &exact-integer 0 (- (&max bv) size))
- (define! result type min max))
- (define-type-checker (set bv idx val)
- (and (check-type bv &bytevector 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (check-type val type min max)
- (< (&max idx) (- (&min bv) size))))
- (define-type-inferrer (set! bv idx val)
- (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
- (restrict! idx &exact-integer 0 (- (&max bv) size))
- (restrict! val type min max))))
-
-(define-syntax-rule (define-short-bytevector-accessors ref set size signed?)
- (define-bytevector-accessors ref set &exact-integer size
- (if signed? (- (ash 1 (1- (* size 8)))) 0)
- (1- (ash 1 (if signed? (1- (* size 8)) (* size 8))))))
-
-(define-short-bytevector-accessors bv-u8-ref bv-u8-set! 1 #f)
-(define-short-bytevector-accessors bv-s8-ref bv-s8-set! 1 #t)
-(define-short-bytevector-accessors bv-u16-ref bv-u16-set! 2 #f)
-(define-short-bytevector-accessors bv-s16-ref bv-s16-set! 2 #t)
-
-;; The range analysis only works on signed 32-bit values, so some limits
-;; are out of range.
-(define-bytevector-accessors bv-u32-ref bv-u32-set! &exact-integer 4 0 +inf.0)
-(define-bytevector-accessors bv-s32-ref bv-s32-set! &exact-integer 4 -inf.0 +inf.0)
-(define-bytevector-accessors bv-u64-ref bv-u64-set! &exact-integer 8 0 +inf.0)
-(define-bytevector-accessors bv-s64-ref bv-s64-set! &exact-integer 8 -inf.0 +inf.0)
-(define-bytevector-accessors bv-f32-ref bv-f32-set! &real 4 -inf.0 +inf.0)
-(define-bytevector-accessors bv-f64-ref bv-f64-set! &real 8 -inf.0 +inf.0)
-
-
-
-
-;;;
-;;; Numbers.
-;;;
-
-;; First, branching primitives with no results.
-(define-simple-type-checker (= &number &number))
-(define-predicate-inferrer (= a b true?)
- (when (and true?
- (zero? (logand (logior (&type a) (&type b)) (lognot &number))))
- (let ((min (max (&min a) (&min b)))
- (max (min (&max a) (&max b))))
- (restrict! a &number min max)
- (restrict! b &number min max))))
-
-(define (restricted-comparison-ranges op type0 min0 max0 type1 min1 max1)
- (define (infer-integer-ranges)
- (match op
- ('< (values min0 (min max0 (1- max1)) (max (1+ min0) min1) max1))
- ('<= (values min0 (min max0 max1) (max min0 min1) max1))
- ('>= (values (max min0 min1) max0 min1 (min max0 max1)))
- ('> (values (max min0 (1+ min1)) max0 min1 (min (1- max0) max1)))))
- (define (infer-real-ranges)
- (match op
- ((or '< '<=) (values min0 (min max0 max1) (max min0 min1) max1))
- ((or '> '>=) (values (max min0 min1) max0 min1 (min max0 max1)))))
- (if (= (logior type0 type1) &exact-integer)
- (infer-integer-ranges)
- (infer-real-ranges)))
-
-(define-syntax-rule (define-comparison-inferrer (op inverse))
- (define-predicate-inferrer (op a b true?)
- (when (zero? (logand (logior (&type a) (&type b)) (lognot &number)))
- (call-with-values
- (lambda ()
- (restricted-comparison-ranges (if true? 'op 'inverse)
- (&type a) (&min a) (&max a)
- (&type b) (&min b) (&max b)))
- (lambda (min0 max0 min1 max1)
- (restrict! a &real min0 max0)
- (restrict! b &real min1 max1))))))
-
-(define-simple-type-checker (< &real &real))
-(define-comparison-inferrer (< >=))
-
-(define-simple-type-checker (<= &real &real))
-(define-comparison-inferrer (<= >))
-
-(define-simple-type-checker (>= &real &real))
-(define-comparison-inferrer (>= <))
-
-(define-simple-type-checker (> &real &real))
-(define-comparison-inferrer (> <=))
-
-;; Arithmetic.
-(define-syntax-rule (define-unary-result! a result min max)
- (let ((min* min)
- (max* max)
- (type (logand (&type a) &number)))
- (cond
- ((not (= type (&type a)))
- ;; Not a number. Punt and do nothing.
- (define! result &all-types -inf.0 +inf.0))
- ;; Complex numbers don't have a range.
- ((eqv? type &complex)
- (define! result &complex -inf.0 +inf.0))
- (else
- (define! result type min* max*)))))
-
-(define-syntax-rule (define-binary-result! a b result closed? min max)
- (let ((min* min)
- (max* max)
- (a-type (logand (&type a) &number))
- (b-type (logand (&type b) &number)))
- (cond
- ((or (not (= a-type (&type a))) (not (= b-type (&type b))))
- ;; One input not a number. Perhaps we end up dispatching to
- ;; GOOPS.
- (define! result &all-types -inf.0 +inf.0))
- ;; Complex and floating-point numbers are contagious.
- ((or (eqv? a-type &complex) (eqv? b-type &complex))
- (define! result &complex -inf.0 +inf.0))
- ((or (eqv? a-type &flonum) (eqv? b-type &flonum))
- (define! result &flonum min* max*))
- ;; Exact integers are closed under some operations.
- ((and closed? (eqv? a-type &exact-integer) (eqv? b-type &exact-integer))
- (define! result &exact-integer min* max*))
- (else
- ;; Fractions may become integers.
- (let ((type (logior a-type b-type)))
- (define! result
- (if (zero? (logand type &fraction))
- type
- (logior type &exact-integer))
- min* max*))))))
-
-(define-simple-type-checker (add &number &number))
-(define-type-inferrer (add a b result)
- (define-binary-result! a b result #t
- (+ (&min a) (&min b))
- (+ (&max a) (&max b))))
-
-(define-simple-type-checker (sub &number &number))
-(define-type-inferrer (sub a b result)
- (define-binary-result! a b result #t
- (- (&min a) (&max b))
- (- (&max a) (&min b))))
-
-(define-simple-type-checker (mul &number &number))
-(define-type-inferrer (mul a b result)
- (let ((min-a (&min a)) (max-a (&max a))
- (min-b (&min b)) (max-b (&max b)))
- (define (nan* a b)
- ;; We only really get +inf.0 at runtime for flonums and compnums.
- ;; If we have inferred that the arguments are not flonums and not
- ;; compnums, then the result of (* +inf.0 0) at range inference
- ;; time is 0 and not +nan.0.
- (if (and (or (and (inf? a) (zero? b))
- (and (zero? a) (inf? b)))
- (not (logtest (logior (&type a) (&type b))
- (logior &flonum &complex))))
- 0
- (* a b)))
- (let ((-- (nan* min-a min-b))
- (-+ (nan* min-a max-b))
- (++ (nan* max-a max-b))
- (+- (nan* max-a min-b)))
- (let ((has-nan? (or (nan? --) (nan? -+) (nan? ++) (nan? +-))))
- (define-binary-result! a b result #t
- (cond
- ((eqv? a b) 0)
- (has-nan? -inf.0)
- (else (min -- -+ ++ +-)))
- (if has-nan?
- +inf.0
- (max -- -+ ++ +-)))))))
-
-(define-type-checker (div a b)
- (and (check-type a &number -inf.0 +inf.0)
- (check-type b &number -inf.0 +inf.0)
- ;; We only know that there will not be an exception if b is not
- ;; zero.
- (not (<= (&min b) 0 (&max b)))))
-(define-type-inferrer (div a b result)
- (let ((min-a (&min a)) (max-a (&max a))
- (min-b (&min b)) (max-b (&max b)))
- (call-with-values
- (lambda ()
- (if (<= min-b 0 max-b)
- ;; If the range of the divisor crosses 0, the result spans
- ;; the whole range.
- (values -inf.0 +inf.0)
- ;; Otherwise min-b and max-b have the same sign, and cannot both
- ;; be infinity.
- (let ((--- (if (inf? min-b) 0 (floor/ min-a min-b)))
- (-+- (if (inf? max-b) 0 (floor/ min-a max-b)))
- (++- (if (inf? max-b) 0 (floor/ max-a max-b)))
- (+-- (if (inf? min-b) 0 (floor/ max-a min-b)))
- (--+ (if (inf? min-b) 0 (ceiling/ min-a min-b)))
- (-++ (if (inf? max-b) 0 (ceiling/ min-a max-b)))
- (+++ (if (inf? max-b) 0 (ceiling/ max-a max-b)))
- (+-+ (if (inf? min-b) 0 (ceiling/ max-a min-b))))
- (values (min (min --- -+- ++- +--)
- (min --+ -++ +++ +-+))
- (max (max --- -+- ++- +--)
- (max --+ -++ +++ +-+))))))
- (lambda (min max)
- (define-binary-result! a b result #f min max)))))
-
-(define-simple-type-checker (add1 &number))
-(define-type-inferrer (add1 a result)
- (define-unary-result! a result (1+ (&min a)) (1+ (&max a))))
-
-(define-simple-type-checker (sub1 &number))
-(define-type-inferrer (sub1 a result)
- (define-unary-result! a result (1- (&min a)) (1- (&max a))))
-
-(define-type-checker (quo a b)
- (and (check-type a &exact-integer -inf.0 +inf.0)
- (check-type b &exact-integer -inf.0 +inf.0)
- ;; We only know that there will not be an exception if b is not
- ;; zero.
- (not (<= (&min b) 0 (&max b)))))
-(define-type-inferrer (quo a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer -inf.0 +inf.0))
-
-(define-type-checker-aliases quo rem)
-(define-type-inferrer (rem a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- ;; Same sign as A.
- (let ((max-abs-rem (1- (max (abs (&min b)) (abs (&max b))))))
- (cond
- ((< (&min a) 0)
- (if (< 0 (&max a))
- (define! result &exact-integer (- max-abs-rem) max-abs-rem)
- (define! result &exact-integer (- max-abs-rem) 0)))
- (else
- (define! result &exact-integer 0 max-abs-rem)))))
-
-(define-type-checker-aliases quo mod)
-(define-type-inferrer (mod a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- ;; Same sign as B.
- (let ((max-abs-mod (1- (max (abs (&min b)) (abs (&max b))))))
- (cond
- ((< (&min b) 0)
- (if (< 0 (&max b))
- (define! result &exact-integer (- max-abs-mod) max-abs-mod)
- (define! result &exact-integer (- max-abs-mod) 0)))
- (else
- (define! result &exact-integer 0 max-abs-mod)))))
-
-;; Predicates.
-(define-syntax-rule (define-number-kind-predicate-inferrer name type)
- (define-type-inferrer (name val result)
- (cond
- ((zero? (logand (&type val) type))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (lognot type)))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0)))))
-(define-number-kind-predicate-inferrer complex? &number)
-(define-number-kind-predicate-inferrer real? &real)
-(define-number-kind-predicate-inferrer rational?
- (logior &exact-integer &fraction))
-(define-number-kind-predicate-inferrer integer?
- (logior &exact-integer &flonum))
-(define-number-kind-predicate-inferrer exact-integer?
- &exact-integer)
-
-(define-simple-type-checker (exact? &number))
-(define-type-inferrer (exact? val result)
- (restrict! val &number -inf.0 +inf.0)
- (cond
- ((zero? (logand (&type val) (logior &exact-integer &fraction)))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (lognot (logior &exact-integer &fraction))))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-simple-type-checker (inexact? &number))
-(define-type-inferrer (inexact? val result)
- (restrict! val &number -inf.0 +inf.0)
- (cond
- ((zero? (logand (&type val) (logior &flonum &complex)))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (logand &number
- (lognot (logior &flonum &complex)))))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-simple-type-checker (inf? &real))
-(define-type-inferrer (inf? val result)
- (restrict! val &real -inf.0 +inf.0)
- (cond
- ((or (zero? (logand (&type val) (logior &flonum &complex)))
- (and (not (inf? (&min val))) (not (inf? (&max val)))))
- (define! result &false 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-type-aliases inf? nan?)
-
-(define-simple-type (even? &exact-integer)
- ((logior &true &false) 0 0))
-(define-type-aliases even? odd?)
-
-;; Bit operations.
-(define-simple-type-checker (ash &exact-integer &exact-integer))
-(define-type-inferrer (ash val count result)
- (define (ash* val count)
- ;; As we can only represent a 32-bit range, don't bother inferring
- ;; shifts that might exceed that range.
- (cond
- ((inf? val) val) ; Preserves sign.
- ((< -32 count 32) (ash val count))
- ((zero? val) 0)
- ((positive? val) +inf.0)
- (else -inf.0)))
- (restrict! val &exact-integer -inf.0 +inf.0)
- (restrict! count &exact-integer -inf.0 +inf.0)
- (let ((-- (ash* (&min val) (&min count)))
- (-+ (ash* (&min val) (&max count)))
- (++ (ash* (&max val) (&max count)))
- (+- (ash* (&max val) (&min count))))
- (define! result &exact-integer
- (min -- -+ ++ +-)
- (max -- -+ ++ +-))))
-
-(define (next-power-of-two n)
- (let lp ((out 1))
- (if (< n out)
- out
- (lp (ash out 1)))))
-
-(define-simple-type-checker (logand &exact-integer &exact-integer))
-(define-type-inferrer (logand a b result)
- (define (logand-min a b)
- (if (and (negative? a) (negative? b))
- (min a b)
- 0))
- (define (logand-max a b)
- (if (and (positive? a) (positive? b))
- (min a b)
- 0))
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (logand-min (&min a) (&min b))
- (logand-max (&max a) (&max b))))
-
-(define-simple-type-checker (logior &exact-integer &exact-integer))
-(define-type-inferrer (logior a b result)
- ;; Saturate all bits of val.
- (define (saturate val)
- (1- (next-power-of-two val)))
- (define (logior-min a b)
- (cond ((and (< a 0) (<= 0 b)) a)
- ((and (< b 0) (<= 0 a)) b)
- (else (max a b))))
- (define (logior-max a b)
- ;; If either operand is negative, just assume the max is -1.
- (cond
- ((or (< a 0) (< b 0)) -1)
- ((or (inf? a) (inf? b)) +inf.0)
- (else (saturate (logior a b)))))
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (logior-min (&min a) (&min b))
- (logior-max (&max a) (&max b))))
-
-;; For our purposes, treat logxor the same as logior.
-(define-type-aliases logior logxor)
-
-(define-simple-type-checker (lognot &exact-integer))
-(define-type-inferrer (lognot a result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (- -1 (&max a))
- (- -1 (&min a))))
-
-(define-simple-type-checker (logtest &exact-integer &exact-integer))
-(define-predicate-inferrer (logtest a b true?)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0))
-
-(define-simple-type-checker (logbit? (&exact-integer 0 +inf.0) &exact-integer))
-(define-type-inferrer (logbit? a b result)
- (let ((a-min (&min a))
- (a-max (&max a))
- (b-min (&min b))
- (b-max (&max b)))
- (if (and (eqv? a-min a-max) (>= a-min 0) (not (inf? a-min))
- (eqv? b-min b-max) (>= b-min 0) (not (inf? b-min)))
- (let ((type (if (logbit? a-min b-min) &true &false)))
- (define! result type 0 0))
- (define! result (logior &true &false) 0 0))))
-
-;; Flonums.
-(define-simple-type-checker (sqrt &number))
-(define-type-inferrer (sqrt x result)
- (let ((type (&type x)))
- (cond
- ((and (zero? (logand type &complex)) (<= 0 (&min x)))
- (define! result
- (logior type &flonum)
- (inexact->exact (floor (sqrt (&min x))))
- (if (inf? (&max x))
- +inf.0
- (inexact->exact (ceiling (sqrt (&max x)))))))
- (else
- (define! result (logior type &flonum &complex) -inf.0 +inf.0)))))
-
-(define-simple-type-checker (abs &real))
-(define-type-inferrer (abs x result)
- (let ((type (&type x)))
- (cond
- ((eqv? type (logand type &number))
- (restrict! x &real -inf.0 +inf.0)
- (define! result (logand type &real)
- (min (abs (&min x)) (abs (&max x)))
- (max (abs (&min x)) (abs (&max x)))))
- (else
- (define! result (logior (logand (&type x) (lognot &number))
- (logand (&type x) &real))
- (max (&min x) 0)
- (max (abs (&min x)) (abs (&max x))))))))
-
-
-
-
-;;;
-;;; Characters.
-;;;
-
-(define-simple-type (char &char &char)
- ((logior &true &false) 0 0))
-(define-type-aliases char char<=? char>=? char>?)
-
-(define-simple-type-checker (integer->char (&exact-integer 0 #x10ffff)))
-(define-type-inferrer (integer->char i result)
- (restrict! i &exact-integer 0 #x10ffff)
- (define! result &char (max (&min i) 0) (min (&max i) #x10ffff)))
-
-(define-simple-type-checker (char->integer &char))
-(define-type-inferrer (char->integer c result)
- (restrict! c &char 0 #x10ffff)
- (define! result &exact-integer (max (&min c) 0) (min (&max c) #x10ffff)))
-
-
-
-
-;;;
-;;; Type flow analysis: the meet (ahem) of the algorithm.
-;;;
-
-(define (infer-types* dfg min-label label-count)
- "Compute types for all variables in @var{fun}. Returns a hash table
-mapping symbols to types."
- (let ((typev (make-vector label-count))
- (idoms (compute-idoms dfg min-label label-count))
- (revisit-label #f)
- (types-changed? #f)
- (saturate-ranges? #f))
- (define (label->idx label) (- label min-label))
-
- (define (get-entry label) (vector-ref typev (label->idx label)))
-
- (define (in-types entry) (vector-ref entry 0))
- (define (out-types entry succ) (vector-ref entry (1+ succ)))
-
- (define (update-in-types! entry types)
- (vector-set! entry 0 types))
- (define (update-out-types! entry succ types)
- (vector-set! entry (1+ succ) types))
-
- (define (prepare-initial-state!)
- ;; The result is a vector with an entry for each label. Each entry
- ;; is a vector. The first slot in the entry vector corresponds to
- ;; the types that flow into the labelled expression. The following
- ;; slot is for the types that flow out to the first successor, and
- ;; so on for additional successors.
- (let lp ((label min-label))
- (when (< label (+ min-label label-count))
- (let* ((nsuccs (match (lookup-cont label dfg)
- (($ $kargs _ _ term)
- (match (find-call term)
- (($ $continue k src (or ($ $branch) ($ $prompt))) 2)
- (_ 1)))
- (($ $kfun src meta self tail clause) (if clause 1 0))
- (($ $kclause arity body alt) (if alt 2 1))
- (($ $kreceive) 1)
- (($ $ktail) 0)))
- (entry (make-vector (1+ nsuccs) #f)))
- (vector-set! typev (label->idx label) entry)
- (lp (1+ label)))))
-
- ;; Initial state: nothing flows into the $kfun.
- (let ((entry (get-entry min-label)))
- (update-in-types! entry empty-intmap)))
-
- (define (adjoin-vars types vars entry)
- (match vars
- (() types)
- ((var . vars)
- (adjoin-vars (adjoin-var types var entry) vars entry))))
-
- (define (infer-primcall types succ name args result)
- (cond
- ((hashq-ref *type-inferrers* name)
- => (lambda (inferrer)
- ;; FIXME: remove the apply?
- ;(pk 'primcall name args result)
- (apply inferrer types succ
- (if result
- (append args (list result))
- args))))
- (result
- (adjoin-var types result all-types-entry))
- (else
- types)))
-
- (define (type-entry-saturating-union a b)
- (cond
- ((type-entry<=? b a) a)
- #;
- ((and (not saturate-ranges?)
- (eqv? (a-type ))
- (type-entry<=? a b)) b)
- (else (make-type-entry
- (let* ((a-type (type-entry-type a))
- (b-type (type-entry-type b))
- (type (logior a-type b-type)))
- (unless (eqv? a-type type)
- (set! types-changed? #t))
- type)
- (let ((a-min (type-entry-clamped-min a))
- (b-min (type-entry-clamped-min b)))
- (if (< b-min a-min)
- (if saturate-ranges? min-fixnum b-min)
- a-min))
- (let ((a-max (type-entry-clamped-max a))
- (b-max (type-entry-clamped-max b)))
- (if (> b-max a-max)
- (if saturate-ranges? max-fixnum b-max)
- a-max))))))
-
- (define (propagate-types! pred-label pred-entry succ-idx succ-label out)
- ;; Update "in" set of continuation.
- (let ((succ-entry (get-entry succ-label)))
- (match (lookup-predecessors succ-label dfg)
- ((_)
- ;; A normal edge.
- (update-in-types! succ-entry out))
- (_
- ;; A control-flow join.
- (let* ((succ-dom-label (vector-ref idoms (label->idx succ-label)))
- (succ-dom-entry (get-entry succ-dom-label))
- (old-in (in-types succ-entry))
- (in (if old-in
- (intmap-intersect old-in out
- type-entry-saturating-union)
- out)))
- ;; If the "in" set changed, update the entry and possibly
- ;; arrange to iterate again.
- (unless (eq? old-in in)
- (update-in-types! succ-entry in)
- ;; If the changed successor is a back-edge, ensure that
- ;; we revisit the function.
- (when (<= succ-label pred-label)
- (unless (and revisit-label (<= revisit-label succ-label))
- ;; (pk 'marking-revisit pred-label succ-label)
- (set! revisit-label succ-label))))))))
- ;; Finally update "out" set for current expression.
- (update-out-types! pred-entry succ-idx out))
-
- (define (visit-exp label entry k types exp)
- (define (propagate! succ-idx succ-label types)
- (propagate-types! label entry succ-idx succ-label types))
- ;; Each of these branches must propagate! to its successors.
- (match exp
- (($ $branch kt ($ $values (arg)))
- ;; The "normal" continuation is the #f branch.
- (let ((types (restrict-var types arg
- (make-type-entry (logior &false &nil)
- 0
- 0))))
- (propagate! 0 k types))
- (let ((types (restrict-var types arg
- (make-type-entry
- (logand &all-types
- (lognot (logior &false &nil)))
- -inf.0 +inf.0))))
- (propagate! 1 kt types)))
- (($ $branch kt ($ $primcall name args))
- ;; The "normal" continuation is the #f branch.
- (let ((types (infer-primcall types 0 name args #f)))
- (propagate! 0 k types))
- (let ((types (infer-primcall types 1 name args #f)))
- (propagate! 1 kt types)))
- (($ $prompt escape? tag handler)
- ;; The "normal" continuation enters the prompt.
- (propagate! 0 k types)
- (propagate! 1 handler types))
- (($ $primcall name args)
- (propagate! 0 k
- (match (lookup-cont k dfg)
- (($ $kargs _ defs)
- (infer-primcall types 0 name args
- (match defs ((var) var) (() #f))))
- (_
- ;(pk 'warning-no-restrictions name)
- types))))
- (($ $values args)
- (match (lookup-cont k dfg)
- (($ $kargs _ defs)
- (let ((in types))
- (let lp ((defs defs) (args args) (out types))
- (match (cons defs args)
- ((() . ())
- (propagate! 0 k out))
- (((def . defs) . (arg . args))
- (lp defs args
- (adjoin-var out def (var-type-entry in arg))))))))
- (_
- (propagate! 0 k types))))
- ((or ($ $call) ($ $callk))
- (propagate! 0 k types))
- (($ $rec names vars funs)
- (let ((proc-type (make-type-entry &procedure -inf.0 +inf.0)))
- (propagate! 0 k (adjoin-vars types vars proc-type))))
- (_
- (match (lookup-cont k dfg)
- (($ $kargs (_) (var))
- (let ((entry (match exp
- (($ $const val)
- (constant-type val))
- ((or ($ $prim) ($ $fun) ($ $closure))
- ;; Could be more precise here.
- (make-type-entry &procedure -inf.0 +inf.0)))))
- (propagate! 0 k (adjoin-var types var entry))))))))
-
- (prepare-initial-state!)
-
- ;; Iterate over all labelled expressions in the function,
- ;; propagating types and ranges to all successors.
- (let lp ((label min-label))
- ;(pk 'visit label)
- (cond
- ((< label (+ min-label label-count))
- (let* ((entry (vector-ref typev (label->idx label)))
- (types (in-types entry)))
- (define (propagate! succ-idx succ-label types)
- (propagate-types! label entry succ-idx succ-label types))
- ;; Add types for new definitions, and restrict types of
- ;; existing variables due to side effects.
- (match (lookup-cont label dfg)
- (($ $kargs names vars term)
- (let visit-term ((term term) (types types))
- (match term
- (($ $letk conts term)
- (visit-term term types))
- (($ $continue k src exp)
- (visit-exp label entry k types exp)))))
- (($ $kreceive arity k)
- (match (lookup-cont k dfg)
- (($ $kargs names vars)
- (propagate! 0 k
- (adjoin-vars types vars all-types-entry)))))
- (($ $kfun src meta self tail clause)
- (let ((types (adjoin-var types self all-types-entry)))
- (match clause
- (#f #f)
- (($ $cont kclause)
- (propagate! 0 kclause types)))))
- (($ $kclause arity ($ $cont kbody ($ $kargs names vars)) alt)
- (propagate! 0 kbody
- (adjoin-vars types vars all-types-entry))
- (match alt
- (#f #f)
- (($ $cont kclause)
- (propagate! 1 kclause types))))
- (($ $ktail) #t)))
-
- ;; And loop.
- (lp (1+ label)))
-
- ;; Iterate until we reach a fixed point.
- (revisit-label
- ;; Once the types have a fixed point, iterate until ranges also
- ;; reach a fixed point, saturating ranges to accelerate
- ;; convergence.
- (unless types-changed?
- (set! saturate-ranges? #t))
- (set! types-changed? #f)
- (let ((label revisit-label))
- (set! revisit-label #f)
- ;(pk 'looping)
- (lp label)))
-
- ;; All done! Return the computed types.
- (else typev)))))
-
-(define-record-type
- (make-type-analysis min-label label-count types)
- type-analysis?
- (min-label type-analysis-min-label)
- (label-count type-analysis-label-count)
- (types type-analysis-types))
-
-(define (infer-types fun dfg)
- ;; Fun must be renumbered.
- (match fun
- (($ $cont min-label ($ $kfun))
- (let ((label-count ((make-local-cont-folder label-count)
- (lambda (k cont label-count) (1+ label-count))
- fun 0)))
- (make-type-analysis min-label label-count
- (infer-types* dfg min-label label-count))))))
-
-(define (lookup-pre-type analysis label def)
- (match analysis
- (($ min-label label-count typev)
- (let* ((entry (vector-ref typev (- label min-label)))
- (tentry (var-type-entry (vector-ref entry 0) def)))
- (values (type-entry-type tentry)
- (type-entry-min tentry)
- (type-entry-max tentry))))))
-
-(define (lookup-post-type analysis label def succ-idx)
- (match analysis
- (($ min-label label-count typev)
- (let* ((entry (vector-ref typev (- label min-label)))
- (tentry (var-type-entry (vector-ref entry (1+ succ-idx)) def)))
- (values (type-entry-type tentry)
- (type-entry-min tentry)
- (type-entry-max tentry))))))
-
-(define (primcall-types-check? analysis label name args)
- (match (hashq-ref *type-checkers* name)
- (#f #f)
- (checker
- (match analysis
- (($ min-label label-count typev)
- (let ((entry (vector-ref typev (- label min-label))))
- (apply checker (vector-ref entry 0) args)))))))
From 90aabcc56556c09a65e4257630b10589aab2d3dd Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 09:58:59 +0200
Subject: [PATCH 019/865] Fix type/range inference for mul
* module/language/cps2/types.scm (mul): Fix nan testing.
---
module/language/cps2/types.scm | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/module/language/cps2/types.scm b/module/language/cps2/types.scm
index 6fca57d73..07da3d6a0 100644
--- a/module/language/cps2/types.scm
+++ b/module/language/cps2/types.scm
@@ -833,16 +833,17 @@ minimum, and maximum."
(define-simple-type-checker (mul &number &number))
(define-type-inferrer (mul a b result)
(let ((min-a (&min a)) (max-a (&max a))
- (min-b (&min b)) (max-b (&max b)))
+ (min-b (&min b)) (max-b (&max b))
+ ;; We only really get +inf.0 at runtime for flonums and
+ ;; compnums. If we have inferred that the arguments are not
+ ;; flonums and not compnums, then the result of (* +inf.0 0) at
+ ;; range inference time is 0 and not +nan.0.
+ (nan-impossible? (not (logtest (logior (&type a) (&type b))
+ (logior &flonum &complex)))))
(define (nan* a b)
- ;; We only really get +inf.0 at runtime for flonums and compnums.
- ;; If we have inferred that the arguments are not flonums and not
- ;; compnums, then the result of (* +inf.0 0) at range inference
- ;; time is 0 and not +nan.0.
(if (and (or (and (inf? a) (zero? b))
(and (zero? a) (inf? b)))
- (not (logtest (logior (&type a) (&type b))
- (logior &flonum &complex))))
+ nan-impossible?)
0
(* a b)))
(let ((-- (nan* min-a min-b))
From 1a819eaaa73b6d3099661d6518f035bd36c79899 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 16 Jul 2015 11:27:14 +0200
Subject: [PATCH 020/865] Reify primitives in CPS2
* module/language/cps/reify-primitives.scm: Remove.
* module/language/cps2/reify-primitives.scm: New file.
* module/Makefile.am: Adapt build.
* module/language/cps/compile-bytecode.scm:
* module/language/cps2/compile-cps.scm: Reify primitives in CPS2 instead
of CPS.
---
module/Makefile.am | 2 +-
module/language/cps/compile-bytecode.scm | 3 +-
module/language/cps/reify-primitives.scm | 178 ----------------------
module/language/cps2/compile-cps.scm | 2 +
module/language/cps2/reify-primitives.scm | 145 ++++++++++++++++++
5 files changed, 149 insertions(+), 181 deletions(-)
delete mode 100644 module/language/cps/reify-primitives.scm
create mode 100644 module/language/cps2/reify-primitives.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 188cc7626..c1c3e5cf0 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -126,7 +126,6 @@ CPS_LANG_SOURCES = \
language/cps/compile-bytecode.scm \
language/cps/dfg.scm \
language/cps/primitives.scm \
- language/cps/reify-primitives.scm \
language/cps/renumber.scm \
language/cps/slot-allocation.scm \
language/cps/spec.scm \
@@ -144,6 +143,7 @@ CPS2_LANG_SOURCES = \
language/cps2/elide-values.scm \
language/cps2/prune-bailouts.scm \
language/cps2/prune-top-level-scopes.scm \
+ language/cps2/reify-primitives.scm \
language/cps2/renumber.scm \
language/cps2/optimize.scm \
language/cps2/simplify.scm \
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index c07db2621..c92c15d01 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -29,7 +29,6 @@
#:use-module (language cps)
#:use-module (language cps dfg)
#:use-module (language cps primitives)
- #:use-module (language cps reify-primitives)
#:use-module (language cps renumber)
#:use-module (language cps slot-allocation)
#:use-module (system vm assembler)
@@ -443,7 +442,7 @@
(compile-entry)))))
(define (compile-bytecode exp env opts)
- (let* ((exp (renumber (reify-primitives exp)))
+ (let* ((exp (renumber exp))
(asm (make-assembler)))
(match exp
(($ $program funs)
diff --git a/module/language/cps/reify-primitives.scm b/module/language/cps/reify-primitives.scm
deleted file mode 100644
index 286fd7c41..000000000
--- a/module/language/cps/reify-primitives.scm
+++ /dev/null
@@ -1,178 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass to reify lone $prim's that were never folded into a
-;;; $primcall, and $primcall's to primitives that don't have a
-;;; corresponding VM op.
-;;;
-;;; Code:
-
-(define-module (language cps reify-primitives)
- #:use-module (ice-9 match)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps primitives)
- #:use-module (language bytecode)
- #:export (reify-primitives))
-
-(define (module-box src module name public? bound? val-proc)
- (let-fresh (kbox) (module-var name-var public?-var bound?-var box)
- (build-cps-term
- ($letconst (('module module-var module)
- ('name name-var name)
- ('public? public?-var public?)
- ('bound? bound?-var bound?))
- ($letk ((kbox ($kargs ('box) (box) ,(val-proc box))))
- ($continue kbox src
- ($primcall 'cached-module-box
- (module-var name-var public?-var bound?-var))))))))
-
-(define (primitive-module name)
- (case name
- ((bytevector?
- bytevector-length
-
- bytevector-u8-ref bytevector-u8-set!
- bytevector-s8-ref bytevector-s8-set!
-
- bytevector-u16-ref bytevector-u16-set!
- bytevector-u16-native-ref bytevector-u16-native-set!
- bytevector-s16-ref bytevector-s16-set!
- bytevector-s16-native-ref bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-u32-set!
- bytevector-u32-native-ref bytevector-u32-native-set!
- bytevector-s32-ref bytevector-s32-set!
- bytevector-s32-native-ref bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-u64-set!
- bytevector-u64-native-ref bytevector-u64-native-set!
- bytevector-s64-ref bytevector-s64-set!
- bytevector-s64-native-ref bytevector-s64-native-set!
-
- bytevector-ieee-single-ref bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
- bytevector-ieee-double-ref bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!)
- '(rnrs bytevectors))
- ((class-of) '(oop goops))
- (else '(guile))))
-
-(define (primitive-ref name k src)
- (module-box #f (primitive-module name) name #f #t
- (lambda (box)
- (build-cps-term
- ($continue k src ($primcall 'box-ref (box)))))))
-
-(define (builtin-ref idx k src)
- (let-fresh () (idx-var)
- (build-cps-term
- ($letconst (('idx idx-var idx))
- ($continue k src
- ($primcall 'builtin-ref (idx-var)))))))
-
-(define (reify-clause ktail)
- (let-fresh (kclause kbody kthrow) (wna false str eol throw)
- (build-cps-cont
- (kclause ($kclause ('() '() #f '() #f)
- (kbody
- ($kargs () ()
- ($letconst (('wna wna 'wrong-number-of-args)
- ('false false #f)
- ('str str "Wrong number of arguments")
- ('eol eol '()))
- ($letk ((kthrow
- ($kargs ('throw) (throw)
- ($continue ktail #f
- ($call throw
- (wna false str eol false))))))
- ,(primitive-ref 'throw kthrow #f)))))
- ,#f)))))
-
-(define (reify-primitives/1 fun single-value-conts)
- (define (visit-clause cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kclause arity body alternate))
- (label ($kclause ,arity ,(visit-cont body)
- ,(and alternate (visit-clause alternate)))))))
- (define (visit-cont cont)
- (rewrite-cps-cont cont
- (($ $cont label ($ $kargs (name) (var) body))
- ,(begin
- (bitvector-set! single-value-conts label #t)
- (build-cps-cont
- (label ($kargs (name) (var) ,(visit-term body))))))
- (($ $cont label ($ $kargs names vars body))
- (label ($kargs names vars ,(visit-term body))))
- (($ $cont)
- ,cont)))
- (define (visit-term term)
- (match term
- (($ $letk conts body)
- ;; Visit continuations before their uses.
- (let ((conts (map visit-cont conts)))
- (build-cps-term
- ($letk ,conts ,(visit-term body)))))
- (($ $continue k src exp)
- (match exp
- (($ $prim name)
- (if (bitvector-ref single-value-conts k)
- (cond
- ((builtin-name->index name)
- => (lambda (idx)
- (builtin-ref idx k src)))
- (else (primitive-ref name k src)))
- (build-cps-term ($continue k src
- ($const *unspecified*)))))
- (($ $primcall 'call-thunk/no-inline (proc))
- (build-cps-term
- ($continue k src ($call proc ()))))
- (($ $primcall name args)
- (cond
- ((or (prim-instruction name) (branching-primitive? name))
- ;; Assume arities are correct.
- term)
- (else
- (let-fresh (k*) (v)
- (build-cps-term
- ($letk ((k* ($kargs (v) (v)
- ($continue k src ($call v args)))))
- ,(cond
- ((builtin-name->index name)
- => (lambda (idx)
- (builtin-ref idx k* src)))
- (else (primitive-ref name k* src)))))))))
- (_ term)))))
-
- (rewrite-cps-cont fun
- (($ $cont label ($ $kfun src meta self (and tail ($ $cont ktail)) #f))
- ;; A case-lambda with no clauses. Reify a clause.
- (label ($kfun src meta self ,tail ,(reify-clause ktail))))
- (($ $cont label ($ $kfun src meta self tail clause))
- (label ($kfun src meta self ,tail ,(visit-clause clause))))))
-
-(define (reify-primitives term)
- (with-fresh-name-state term
- (let ((single-value-conts (make-bitvector (label-counter) #f)))
- (rewrite-cps-term term
- (($ $program procs)
- ($program ,(map (lambda (cont)
- (reify-primitives/1 cont single-value-conts))
- procs)))))))
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
index 85b00c95c..ee6e3d5bb 100644
--- a/module/language/cps2/compile-cps.scm
+++ b/module/language/cps2/compile-cps.scm
@@ -29,6 +29,7 @@
#:use-module (language cps2 utils)
#:use-module (language cps2 closure-conversion)
#:use-module (language cps2 optimize)
+ #:use-module (language cps2 reify-primitives)
#:use-module (language cps2 renumber)
#:use-module (language cps intmap)
#:export (compile-cps))
@@ -123,5 +124,6 @@
(set! exp (optimize-higher-order-cps exp opts))
(set! exp (convert-closures exp))
(set! exp (optimize-first-order-cps exp opts))
+ (set! exp (reify-primitives exp))
(set! exp (renumber exp))
(values (conts->fun* exp) env env))
diff --git a/module/language/cps2/reify-primitives.scm b/module/language/cps2/reify-primitives.scm
new file mode 100644
index 000000000..b5f62d456
--- /dev/null
+++ b/module/language/cps2/reify-primitives.scm
@@ -0,0 +1,145 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A pass to reify lone $prim's that were never folded into a
+;;; $primcall, and $primcall's to primitives that don't have a
+;;; corresponding VM op.
+;;;
+;;; Code:
+
+(define-module (language cps2 reify-primitives)
+ #:use-module (ice-9 match)
+ #:use-module (language cps2)
+ #:use-module (language cps2 utils)
+ #:use-module (language cps2 with-cps)
+ #:use-module (language cps primitives)
+ #:use-module (language cps intmap)
+ #:use-module (language bytecode)
+ #:export (reify-primitives))
+
+(define (module-box cps src module name public? bound? val-proc)
+ (with-cps cps
+ (letv box)
+ (let$ body (val-proc box))
+ (letk kbox ($kargs ('box) (box) ,body))
+ ($ (with-cps-constants ((module module)
+ (name name)
+ (public? public?)
+ (bound? bound?))
+ (build-term ($continue kbox src
+ ($primcall 'cached-module-box
+ (module name public? bound?))))))))
+
+(define (primitive-module name)
+ (case name
+ ((bytevector?
+ bytevector-length
+
+ bytevector-u8-ref bytevector-u8-set!
+ bytevector-s8-ref bytevector-s8-set!
+
+ bytevector-u16-ref bytevector-u16-set!
+ bytevector-u16-native-ref bytevector-u16-native-set!
+ bytevector-s16-ref bytevector-s16-set!
+ bytevector-s16-native-ref bytevector-s16-native-set!
+
+ bytevector-u32-ref bytevector-u32-set!
+ bytevector-u32-native-ref bytevector-u32-native-set!
+ bytevector-s32-ref bytevector-s32-set!
+ bytevector-s32-native-ref bytevector-s32-native-set!
+
+ bytevector-u64-ref bytevector-u64-set!
+ bytevector-u64-native-ref bytevector-u64-native-set!
+ bytevector-s64-ref bytevector-s64-set!
+ bytevector-s64-native-ref bytevector-s64-native-set!
+
+ bytevector-ieee-single-ref bytevector-ieee-single-set!
+ bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
+ bytevector-ieee-double-ref bytevector-ieee-double-set!
+ bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!)
+ '(rnrs bytevectors))
+ ((class-of) '(oop goops))
+ (else '(guile))))
+
+(define (primitive-ref cps name k src)
+ (module-box cps src (primitive-module name) name #f #t
+ (lambda (cps box)
+ (with-cps cps
+ (build-term
+ ($continue k src ($primcall 'box-ref (box))))))))
+
+(define (builtin-ref cps idx k src)
+ (with-cps cps
+ ($ (with-cps-constants ((idx idx))
+ (build-term
+ ($continue k src ($primcall 'builtin-ref (idx))))))))
+
+(define (reify-clause cps ktail)
+ (with-cps cps
+ (letv throw)
+ (let$ throw-body
+ (with-cps-constants ((wna 'wrong-number-of-args)
+ (false #f)
+ (str "Wrong number of arguments")
+ (eol '()))
+ (build-term
+ ($continue ktail #f
+ ($call throw (wna false str eol false))))))
+ (letk kthrow ($kargs ('throw) (throw) ,throw-body))
+ (let$ body (primitive-ref 'throw kthrow #f))
+ (letk kbody ($kargs () () ,body))
+ (letk kclause ($kclause ('() '() #f '() #f) kbody #f))
+ kclause))
+
+(define (reify-primitives cps)
+ (define (visit-cont label cont cps)
+ (define (resolve-prim cps name k src)
+ (cond
+ ((builtin-name->index name)
+ => (lambda (idx) (builtin-ref cps idx k src)))
+ (else
+ (primitive-ref cps name k src))))
+ (match cont
+ (($ $kfun src meta self tail #f)
+ (with-cps cps
+ (let$ clause (reify-clause tail))
+ (setk label ($kfun src meta self tail clause))))
+ (($ $kargs names vars ($ $continue k src ($ $prim name)))
+ (with-cps cps
+ (let$ body (resolve-prim name k src))
+ (setk label ($kargs names vars ,body))))
+ (($ $kargs names vars
+ ($ $continue k src ($ $primcall 'call-thunk/no-inline (proc))))
+ (with-cps cps
+ (setk label ($kargs names vars ($continue k src ($call proc ()))))))
+ (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
+ (if (or (prim-instruction name) (branching-primitive? name))
+ ;; Assume arities are correct.
+ cps
+ (with-cps cps
+ (letv proc)
+ (letk kproc ($kargs ('proc) (proc)
+ ($continue k src ($call proc args))))
+ (let$ body (resolve-prim name kproc src))
+ (setk label ($kargs names vars ,body)))))
+ (_ cps)))
+
+ (with-fresh-name-state cps
+ (persistent-intmap (intmap-fold visit-cont cps cps))))
From 3b1d316383a76a2933347ed07a3bd9ac3398ee6b Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 19 Jul 2015 12:20:01 +0200
Subject: [PATCH 021/865] Add intset-prev and intset-fold-right
* module/language/cps/intset.scm (intset-prev): New function.
(make-intset-folder): Add forward? argument like make-intmap-folder.
(intset-fold-right): New function.
---
module/language/cps/intset.scm | 66 +++++++++++++++++++++++++++-------
1 file changed, 54 insertions(+), 12 deletions(-)
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
index 7a16464f2..8c7a23bf3 100644
--- a/module/language/cps/intset.scm
+++ b/module/language/cps/intset.scm
@@ -40,7 +40,9 @@
intset-remove
intset-ref
intset-next
+ intset-prev
intset-fold
+ intset-fold-right
intset-union
intset-intersect
intset-subtract
@@ -391,31 +393,62 @@
(assert-readable! edit)
(next min shift root))))
-(define-syntax-rule (make-intset-folder seed ...)
+(define* (intset-prev bs #:optional i)
+ (define (visit-leaf node i)
+ (let lp ((idx (logand i *leaf-mask*)))
+ (if (logbit? idx node)
+ (logior (logand i (lognot *leaf-mask*)) idx)
+ (let ((idx (1- idx)))
+ (and (<= 0 idx) (lp idx))))))
+ (define (visit-branch node shift i)
+ (let lp ((i i) (idx (logand (ash i (- shift)) *branch-mask*)))
+ (and (<= 0 idx)
+ (or (let ((node (vector-ref node idx)))
+ (and node (visit-node node shift i)))
+ (lp (1- (round-down i shift)) (1- idx))))))
+ (define (visit-node node shift i)
+ (if (= shift *leaf-bits*)
+ (visit-leaf node i)
+ (visit-branch node (- shift *branch-bits*) i)))
+ (define (prev min shift root)
+ (let ((i (if (and i (<= i (+ min (ash 1 shift))))
+ (- i min)
+ (1- (ash 1 shift)))))
+ (and root (<= 0 i)
+ (let ((i (visit-node root shift i)))
+ (and i (+ min i))))))
+ (match bs
+ (($ min shift root)
+ (prev min shift root))
+ (($ min shift root edit)
+ (assert-readable! edit)
+ (prev min shift root))))
+
+(define-syntax-rule (make-intset-folder forward? seed ...)
(lambda (f set seed ...)
(define (visit-branch node shift min seed ...)
(cond
((= shift *leaf-bits*)
- (let lp ((i 0) (seed seed) ...)
- (if (< i *leaf-size*)
+ (let lp ((i (if forward? 0 (1- *leaf-size*))) (seed seed) ...)
+ (if (if forward? (< i *leaf-size*) (<= 0 i))
(if (logbit? i node)
(call-with-values (lambda () (f (+ i min) seed ...))
(lambda (seed ...)
- (lp (1+ i) seed ...)))
- (lp (1+ i) seed ...))
+ (lp (if forward? (1+ i) (1- i)) seed ...)))
+ (lp (if forward? (1+ i) (1- i)) seed ...))
(values seed ...))))
(else
(let ((shift (- shift *branch-bits*)))
- (let lp ((i 0) (seed seed) ...)
- (if (< i *branch-size*)
+ (let lp ((i (if forward? 0 (1- *branch-size*))) (seed seed) ...)
+ (if (if forward? (< i *branch-size*) (<= 0 i))
(let ((elt (vector-ref node i)))
(if elt
(call-with-values
(lambda ()
(visit-branch elt shift (+ min (ash i shift)) seed ...))
(lambda (seed ...)
- (lp (1+ i) seed ...)))
- (lp (1+ i) seed ...)))
+ (lp (if forward? (1+ i) (1- i)) seed ...)))
+ (lp (if forward? (1+ i) (1- i)) seed ...)))
(values seed ...)))))))
(match set
(($ min shift root)
@@ -428,11 +461,20 @@
(define intset-fold
(case-lambda
((f set seed)
- ((make-intset-folder seed) f set seed))
+ ((make-intset-folder #t seed) f set seed))
((f set s0 s1)
- ((make-intset-folder s0 s1) f set s0 s1))
+ ((make-intset-folder #t s0 s1) f set s0 s1))
((f set s0 s1 s2)
- ((make-intset-folder s0 s1 s2) f set s0 s1 s2))))
+ ((make-intset-folder #t s0 s1 s2) f set s0 s1 s2))))
+
+(define intset-fold-right
+ (case-lambda
+ ((f set seed)
+ ((make-intset-folder #f seed) f set seed))
+ ((f set s0 s1)
+ ((make-intset-folder #f s0 s1) f set s0 s1))
+ ((f set s0 s1 s2)
+ ((make-intset-folder #f s0 s1 s2) f set s0 s1 s2))))
(define (intset-size shift root)
(cond
From 19024bdc2715949bf65a270118fafe2057a84193 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 19 Jul 2015 12:21:31 +0200
Subject: [PATCH 022/865] Utils refactors
* module/language/cps2/utils.scm (compute-successors): kfun is
optional.
(compute-sorted-strongly-connected-components): New function, moved
from split-rec.scm. Doesn't assume that 0 is a free node identifier.
* module/language/cps2/split-rec.scm
(compute-sorted-strongly-connected-components): Remove, use utils.scm
version instead.
* module/language/cps2/closure-conversion.scm (intset-select): Remove
unused function.
---
module/language/cps2/closure-conversion.scm | 10 ----
module/language/cps2/split-rec.scm | 45 -----------------
module/language/cps2/utils.scm | 55 ++++++++++++++++++++-
3 files changed, 54 insertions(+), 56 deletions(-)
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
index 9e3a099ce..7de34482f 100644
--- a/module/language/cps2/closure-conversion.scm
+++ b/module/language/cps2/closure-conversion.scm
@@ -443,16 +443,6 @@ variable, until we reach a fixed point on the free-vars map."
((= start i) idx)
(else (lp (1+ idx) (1+ start)))))))
-(define (intmap-select map set)
- (persistent-intmap
- (intmap-fold
- (lambda (k v out)
- (if (intset-ref set k)
- (intmap-add! out k v)
- out))
- map
- empty-intmap)))
-
(define (intset-count set)
(intset-fold (lambda (_ count) (1+ count)) set 0))
diff --git a/module/language/cps2/split-rec.scm b/module/language/cps2/split-rec.scm
index 20cb516a2..aeb1c6397 100644
--- a/module/language/cps2/split-rec.scm
+++ b/module/language/cps2/split-rec.scm
@@ -105,51 +105,6 @@ references."
(persistent-intset defs)))))))
(visit-fun kfun))
-(define (compute-sorted-strongly-connected-components edges)
- (define nodes
- (intmap-keys edges))
- ;; Add a "start" node that links to all nodes in the graph, and then
- ;; remove it from the result.
- (define components
- (intmap-remove
- (compute-strongly-connected-components (intmap-add edges 0 nodes) 0)
- 0))
- (define node-components
- (intmap-fold (lambda (id nodes out)
- (intset-fold (lambda (node out) (intmap-add out node id))
- nodes out))
- components
- empty-intmap))
- (define (node-component node)
- (intmap-ref node-components node))
- (define (component-successors id nodes)
- (intset-remove
- (intset-fold (lambda (node out)
- (intset-fold
- (lambda (successor out)
- (intset-add out (node-component successor)))
- (intmap-ref edges node)
- out))
- nodes
- empty-intset)
- id))
- (define component-edges
- (intmap-map component-successors components))
- (define preds
- (invert-graph component-edges))
- (define roots
- (intmap-fold (lambda (id succs out)
- (if (eq? empty-intset succs)
- (intset-add out id)
- out))
- component-edges
- empty-intset))
- ;; As above, add a "start" node that links to the roots, and remove it
- ;; from the result.
- (match (compute-reverse-post-order (intmap-add preds 0 roots) 0)
- ((0 . ids)
- (map (lambda (id) (intmap-ref components id)) ids))))
-
(define (compute-split fns free-vars)
(define (get-free kfun)
;; It's possible for a fun to have been skipped by
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
index d96b776c9..e62966e40 100644
--- a/module/language/cps2/utils.scm
+++ b/module/language/cps2/utils.scm
@@ -54,6 +54,7 @@
compute-predecessors
compute-reverse-post-order
compute-strongly-connected-components
+ compute-sorted-strongly-connected-components
compute-idoms
compute-dom-edges
))
@@ -270,7 +271,7 @@ intset."
visited))
lp)))))
-(define (compute-successors conts kfun)
+(define* (compute-successors conts #:optional (kfun (intmap-next conts)))
(define (visit label succs)
(let visit ((label kfun) (succs empty-intmap))
(define (propagate0)
@@ -374,6 +375,58 @@ partitioning the labels into strongly connected components (SCCs)."
(fold visit-scc empty-intmap (compute-reverse-post-order succs start))
empty-intmap)))
+(define (compute-sorted-strongly-connected-components edges)
+ "Given a LABEL->SUCCESSOR... graph, return a list of strongly
+connected components in sorted order."
+ (define nodes
+ (intmap-keys edges))
+ ;; Add a "start" node that links to all nodes in the graph, and then
+ ;; remove it from the result.
+ (define start
+ (if (eq? nodes empty-intset)
+ 0
+ (1+ (intset-prev nodes))))
+ (define components
+ (intmap-remove
+ (compute-strongly-connected-components (intmap-add edges start nodes)
+ start)
+ start))
+ (define node-components
+ (intmap-fold (lambda (id nodes out)
+ (intset-fold (lambda (node out) (intmap-add out node id))
+ nodes out))
+ components
+ empty-intmap))
+ (define (node-component node)
+ (intmap-ref node-components node))
+ (define (component-successors id nodes)
+ (intset-remove
+ (intset-fold (lambda (node out)
+ (intset-fold
+ (lambda (successor out)
+ (intset-add out (node-component successor)))
+ (intmap-ref edges node)
+ out))
+ nodes
+ empty-intset)
+ id))
+ (define component-edges
+ (intmap-map component-successors components))
+ (define preds
+ (invert-graph component-edges))
+ (define roots
+ (intmap-fold (lambda (id succs out)
+ (if (eq? empty-intset succs)
+ (intset-add out id)
+ out))
+ component-edges
+ empty-intset))
+ ;; As above, add a "start" node that links to the roots, and remove it
+ ;; from the result.
+ (match (compute-reverse-post-order (intmap-add preds start roots) start)
+ (((? (lambda (id) (eqv? id start))) . ids)
+ (map (lambda (id) (intmap-ref components id)) ids))))
+
;; Precondition: For each function in CONTS, the continuation names are
;; topologically sorted.
(define (compute-idoms conts kfun)
From 365296a866598563a3641b541ea30fafa1d3dd32 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 19 Jul 2015 12:23:05 +0200
Subject: [PATCH 023/865] CPS1 slot-allocation simplification
* module/language/cps/slot-allocation.scm (allocate-slots): Don't pass
around nargs, as it's not used.
---
module/language/cps/slot-allocation.scm | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
index d8cbd15ba..400f9e390 100644
--- a/module/language/cps/slot-allocation.scm
+++ b/module/language/cps/slot-allocation.scm
@@ -281,7 +281,7 @@ are comparable with eqv?. A tmp slot may be used."
0
(1- (find-first-trailing-zero live-slots))))
- (define (recompute-live-slots k nargs)
+ (define (recompute-live-slots k)
(let ((in (dfa-k-in dfa (label->idx k))))
(let lp ((v 0) (live-slots 0))
(let ((v (intset-next in v)))
@@ -589,10 +589,10 @@ are comparable with eqv?. A tmp slot may be used."
(hashq-set! call-allocations label
(make-call-allocation #f moves #f))))))
- (define (allocate-prompt label k handler nargs)
+ (define (allocate-prompt label k handler)
(match (lookup-cont handler dfg)
(($ $kreceive arity kargs)
- (let* ((handler-live (recompute-live-slots handler nargs))
+ (let* ((handler-live (recompute-live-slots handler))
(proc-slot (compute-prompt-handler-proc-slot handler-live))
(result-vars (vector-ref defv (label->idx kargs)))
(value-slots (map (cut + proc-slot 1 <>)
@@ -618,8 +618,8 @@ are comparable with eqv?. A tmp slot may be used."
;; This traversal will visit definitions before uses, as
;; definitions dominate uses and a block's dominator will appear
;; before it, in reverse post-order.
- (define (visit-clause n nargs live)
- (let lp ((n n) (live (recompute-live-slots (idx->label n) nargs)))
+ (define (visit-clause n live)
+ (let lp ((n n) (live (recompute-live-slots (idx->label n))))
(define (kill-dead live vars-by-label-idx pred)
(fold (lambda (v live)
(let ((slot (vector-ref slots v)))
@@ -636,7 +636,7 @@ are comparable with eqv?. A tmp slot may be used."
n
(let* ((label (idx->label n))
(live (if (control-point? label dfg)
- (recompute-live-slots label nargs)
+ (recompute-live-slots label)
live))
(live (kill-dead-defs (allocate-defs! n live)))
(post-live (kill-dead-uses live)))
@@ -649,7 +649,7 @@ are comparable with eqv?. A tmp slot may be used."
(define (compute-k-live k)
(match (lookup-predecessors k dfg)
((_) post-live)
- (_ (recompute-live-slots k nargs))))
+ (_ (recompute-live-slots k))))
(let ((uses (vector-ref usev n)))
(match (find-call body)
(($ $continue k src (or ($ $call) ($ $callk)))
@@ -658,7 +658,7 @@ are comparable with eqv?. A tmp slot may be used."
(($ $continue k src ($ $values))
(allocate-values label k uses live (compute-k-live k)))
(($ $continue k src ($ $prompt escape? tag handler))
- (allocate-prompt label k handler nargs))
+ (allocate-prompt label k handler))
(_ #f)))
(lp (1+ n) post-live))
((or ($ $kreceive) ($ $ktail))
@@ -675,7 +675,6 @@ are comparable with eqv?. A tmp slot may be used."
(error "Unexpected label order"))
(let* ((nargs (length names))
(next (visit-clause (1+ n)
- nargs
(fold allocate! live
(vector-ref defv (1+ n))
(cdr (iota (1+ nargs)))))))
From f63b2e4814761f805d3e7897bc543ebd3543b008 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 20 Jul 2015 10:57:52 +0200
Subject: [PATCH 024/865] More slot-allocation simplification
* module/language/cps/slot-allocation.scm (allocate-slots): Remove
unreachable clause.
---
module/language/cps/slot-allocation.scm | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
index 400f9e390..c60f0f22f 100644
--- a/module/language/cps/slot-allocation.scm
+++ b/module/language/cps/slot-allocation.scm
@@ -542,19 +542,7 @@ are comparable with eqv?. A tmp slot may be used."
(hashq-set! call-allocations label
(make-call-allocation proc-slot arg-moves dead-slot-map))
(hashq-set! call-allocations k
- (make-call-allocation proc-slot result-moves #f))))
-
- (_
- (let* ((proc-slot (compute-call-proc-slot post-live))
- (call-slots (map (cut + proc-slot <>) (iota (length uses))))
- (pre-live (fold allocate! pre-live uses call-slots))
- (arg-moves (parallel-move (map (cut vector-ref slots <>) uses)
- call-slots
- (compute-tmp-slot pre-live
- call-slots))))
- (bump-nlocals! (+ proc-slot (length uses)))
- (hashq-set! call-allocations label
- (make-call-allocation proc-slot arg-moves #f))))))
+ (make-call-allocation proc-slot result-moves #f))))))
(define (allocate-values label k uses pre-live post-live)
(match (lookup-cont k dfg)
From 08cf30f2a0fc6c9e0851e229a11c09ab9aaacec0 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 20 Jul 2015 16:24:49 +0200
Subject: [PATCH 025/865] Fix error printing some wrong-num-args backtraces
* module/system/repl/debug.scm (print-frame): Pass #:top-frame? #t for
the top frame.
* module/system/vm/frame.scm (available-bindings): Be permissive and
allow #:top-frame? #f even when the IP is at the start of the
function.
---
module/system/repl/debug.scm | 5 +++--
module/system/vm/frame.scm | 5 ++++-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/module/system/repl/debug.scm b/module/system/repl/debug.scm
index 300145d16..6fff660e5 100644
--- a/module/system/repl/debug.scm
+++ b/module/system/repl/debug.scm
@@ -1,6 +1,6 @@
;;; Guile VM debugging facilities
-;;; Copyright (C) 2001, 2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.
+;;; Copyright (C) 2001, 2009, 2010, 2011, 2013, 2014, 2015 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
@@ -134,7 +134,8 @@
(format port "~&In ~a:~&" file))
(format port "~9@a~:[~*~3_~;~3d~] ~v:@y~%"
(if line (format #f "~a:~a" line col) "")
- index index width (frame-call-representation frame))
+ index index width
+ (frame-call-representation frame #:top-frame? (zero? index)))
(if full?
(print-locals frame #:width width
#:per-line-prefix " "))))
diff --git a/module/system/vm/frame.scm b/module/system/vm/frame.scm
index ac5fbf6f5..b84f6683e 100644
--- a/module/system/vm/frame.scm
+++ b/module/system/vm/frame.scm
@@ -206,7 +206,10 @@
(when (< offset 0)
(error "ip did not correspond to an instruction boundary?"))
(if (zero? offset)
- (let ((live (if top-frame?
+ ;; It shouldn't be the case that both OFFSET and N are zero
+ ;; but TOP-FRAME? is false. Still, it could happen, as is
+ ;; currently the case in frame-arguments.
+ (let ((live (if (or top-frame? (zero? n))
(vector-ref inv n)
;; If we're not at a top frame, the IP points
;; to the continuation -- but we haven't
From ff2beb186ef52286214ccd2e52c6262c84c3035f Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 21 Jul 2015 17:48:22 +0200
Subject: [PATCH 026/865] Fix bad return shuffles for multiply-used $kreceive
conts
* module/language/cps2/reify-primitives.scm (uniquify-receive):
(reify-primitives): Ensure that $kreceive conts can have only one
predecessor. Otherwise return shuffles are incorrectly allocated.
---
module/language/cps2/reify-primitives.scm | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/module/language/cps2/reify-primitives.scm b/module/language/cps2/reify-primitives.scm
index b5f62d456..55409bfc1 100644
--- a/module/language/cps2/reify-primitives.scm
+++ b/module/language/cps2/reify-primitives.scm
@@ -108,6 +108,16 @@
(letk kclause ($kclause ('() '() #f '() #f) kbody #f))
kclause))
+;; A $kreceive continuation should have only one predecessor.
+(define (uniquify-receive cps k)
+ (match (intmap-ref cps k)
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (with-cps cps
+ (letk k ($kreceive req rest kargs))
+ k))
+ (_
+ (with-cps cps k))))
+
(define (reify-primitives cps)
(define (visit-cont label cont cps)
(define (resolve-prim cps name k src)
@@ -123,6 +133,7 @@
(setk label ($kfun src meta self tail clause))))
(($ $kargs names vars ($ $continue k src ($ $prim name)))
(with-cps cps
+ (let$ k (uniquify-receive k))
(let$ body (resolve-prim name k src))
(setk label ($kargs names vars ,body))))
(($ $kargs names vars
@@ -135,10 +146,21 @@
cps
(with-cps cps
(letv proc)
+ (let$ k (uniquify-receive k))
(letk kproc ($kargs ('proc) (proc)
($continue k src ($call proc args))))
(let$ body (resolve-prim name kproc src))
(setk label ($kargs names vars ,body)))))
+ (($ $kargs names vars ($ $continue k src ($ $call proc args)))
+ (with-cps cps
+ (let$ k (uniquify-receive k))
+ (setk label ($kargs names vars
+ ($continue k src ($call proc args))))))
+ (($ $kargs names vars ($ $continue k src ($ $callk k* proc args)))
+ (with-cps cps
+ (let$ k (uniquify-receive k))
+ (setk label ($kargs names vars
+ ($continue k src ($callk k* proc args))))))
(_ cps)))
(with-fresh-name-state cps
From 2df454b95b67029e28bbe041885001ce4a14adfd Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 16:59:47 +0200
Subject: [PATCH 027/865] intset-intersect bugfix
* module/language/cps/intset.scm (intset-intersect): Remove new-leaf
procedure, inlining to single call site. An empty intersection
properly produces #f so that the set can be pruned.
---
module/language/cps/intset.scm | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
index 8c7a23bf3..bb35a23d3 100644
--- a/module/language/cps/intset.scm
+++ b/module/language/cps/intset.scm
@@ -102,7 +102,6 @@
(root transient-intset-root set-transient-intset-root!)
(edit transient-intset-edit set-transient-intset-edit!))
-(define (new-leaf) 0)
(define-inlinable (clone-leaf-and-set leaf i val)
(if val
(if leaf
@@ -573,10 +572,10 @@
(else (make-intset a-min a-shift root)))))))))
(define (intset-intersect a b)
- (define tmp (new-leaf))
;; Intersect leaves.
(define (intersect-leaves a b)
- (logand a b))
+ (let ((leaf (logand a b)))
+ (if (eqv? leaf 0) #f leaf)))
;; Intersect A and B from index I; the result will be fresh.
(define (intersect-branches/fresh shift a b i fresh)
(let lp ((i 0))
From 16d92c566fe23e50359965056ccfe25b2ae92dd5 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 17:00:34 +0200
Subject: [PATCH 028/865] Fix CPS2 compute-successors
* module/language/cps2/utils.scm (compute-successors): Propagate $kfun
to $ktail so that we don't leave off the tail if the body iloops.
---
module/language/cps2/utils.scm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
index e62966e40..eae6b69c6 100644
--- a/module/language/cps2/utils.scm
+++ b/module/language/cps2/utils.scm
@@ -293,8 +293,8 @@ intset."
(propagate1 k))
(($ $kfun src meta self tail clause)
(if clause
- (propagate1 clause)
- (propagate0)))
+ (propagate2 clause tail)
+ (propagate1 tail)))
(($ $kclause arity kbody kalt)
(if kalt
(propagate2 kbody kalt)
From 910054bfbc628843235db3a9d315986280f09bcd Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 17:01:19 +0200
Subject: [PATCH 029/865] Slot allocation and bytecode compilation from CPS2.
* module/language/cps2/compile-bytecode.scm: New file.
* module/language/cps2/slot-allocation.scm: New file.
* module/Makefile.am: Add new files.
---
module/Makefile.am | 2 +
module/language/cps2/compile-bytecode.scm | 433 ++++++++++
module/language/cps2/slot-allocation.scm | 995 ++++++++++++++++++++++
3 files changed, 1430 insertions(+)
create mode 100644 module/language/cps2/compile-bytecode.scm
create mode 100644 module/language/cps2/slot-allocation.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index c1c3e5cf0..801f466cb 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -134,6 +134,7 @@ CPS_LANG_SOURCES = \
CPS2_LANG_SOURCES = \
language/cps2.scm \
language/cps2/closure-conversion.scm \
+ language/cps2/compile-bytecode.scm \
language/cps2/compile-cps.scm \
language/cps2/constructors.scm \
language/cps2/contification.scm \
@@ -148,6 +149,7 @@ CPS2_LANG_SOURCES = \
language/cps2/optimize.scm \
language/cps2/simplify.scm \
language/cps2/self-references.scm \
+ language/cps2/slot-allocation.scm \
language/cps2/spec.scm \
language/cps2/specialize-primcalls.scm \
language/cps2/split-rec.scm \
diff --git a/module/language/cps2/compile-bytecode.scm b/module/language/cps2/compile-bytecode.scm
new file mode 100644
index 000000000..a39c9f222
--- /dev/null
+++ b/module/language/cps2/compile-bytecode.scm
@@ -0,0 +1,433 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Compiling CPS to bytecode. The result is in the bytecode language,
+;;; which happens to be an ELF image as a bytecode.
+;;;
+;;; Code:
+
+(define-module (language cps2 compile-bytecode)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (language cps2)
+ #:use-module (language cps primitives)
+ #:use-module (language cps2 slot-allocation)
+ #:use-module (language cps2 utils)
+ #:use-module (language cps2 closure-conversion)
+ #:use-module (language cps2 optimize)
+ #:use-module (language cps2 reify-primitives)
+ #:use-module (language cps2 renumber)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:use-module (system vm assembler)
+ #:export (compile-bytecode))
+
+(define (kw-arg-ref args kw default)
+ (match (memq kw args)
+ ((_ val . _) val)
+ (_ default)))
+
+(define (intmap-for-each f map)
+ (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
+
+(define (intmap-select map set)
+ (persistent-intmap
+ (intset-fold
+ (lambda (k out)
+ (intmap-add! out k (intmap-ref map k)))
+ set
+ empty-intmap)))
+
+(define (compile-function cps asm)
+ (let ((allocation (allocate-slots cps))
+ (frame-size #f))
+ (define (maybe-slot sym)
+ (lookup-maybe-slot sym allocation))
+
+ (define (slot sym)
+ (lookup-slot sym allocation))
+
+ (define (constant sym)
+ (lookup-constant-value sym allocation))
+
+ (define (maybe-mov dst src)
+ (unless (= dst src)
+ (emit-mov asm dst src)))
+
+ (define (compile-tail label exp)
+ ;; There are only three kinds of expressions in tail position:
+ ;; tail calls, multiple-value returns, and single-value returns.
+ (match exp
+ (($ $call proc args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-tail-call asm (1+ (length args))))
+ (($ $callk k proc args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-tail-call-label asm (1+ (length args)) k))
+ (($ $values ())
+ (emit-reset-frame asm 1)
+ (emit-return-values asm))
+ (($ $values (arg))
+ (if (maybe-slot arg)
+ (emit-return asm (slot arg))
+ (begin
+ (emit-load-constant asm 1 (constant arg))
+ (emit-return asm 1))))
+ (($ $values args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-reset-frame asm (1+ (length args)))
+ (emit-return-values asm))
+ (($ $primcall 'return (arg))
+ (emit-return asm (slot arg)))))
+
+ (define (compile-value label exp dst)
+ (match exp
+ (($ $values (arg))
+ (maybe-mov dst (slot arg)))
+ (($ $const exp)
+ (emit-load-constant asm dst exp))
+ (($ $closure k 0)
+ (emit-load-static-procedure asm dst k))
+ (($ $closure k nfree)
+ (emit-make-closure asm dst k nfree))
+ (($ $primcall 'current-module)
+ (emit-current-module asm dst))
+ (($ $primcall 'cached-toplevel-box (scope name bound?))
+ (emit-cached-toplevel-box asm dst (constant scope) (constant name)
+ (constant bound?)))
+ (($ $primcall 'cached-module-box (mod name public? bound?))
+ (emit-cached-module-box asm dst (constant mod) (constant name)
+ (constant public?) (constant bound?)))
+ (($ $primcall 'resolve (name bound?))
+ (emit-resolve asm dst (constant bound?) (slot name)))
+ (($ $primcall 'free-ref (closure idx))
+ (emit-free-ref asm dst (slot closure) (constant idx)))
+ (($ $primcall 'vector-ref (vector index))
+ (emit-vector-ref asm dst (slot vector) (slot index)))
+ (($ $primcall 'make-vector (length init))
+ (emit-make-vector asm dst (slot length) (slot init)))
+ (($ $primcall 'make-vector/immediate (length init))
+ (emit-make-vector/immediate asm dst (constant length) (slot init)))
+ (($ $primcall 'vector-ref/immediate (vector index))
+ (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
+ (($ $primcall 'allocate-struct (vtable nfields))
+ (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
+ (($ $primcall 'allocate-struct/immediate (vtable nfields))
+ (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
+ (($ $primcall 'struct-ref (struct n))
+ (emit-struct-ref asm dst (slot struct) (slot n)))
+ (($ $primcall 'struct-ref/immediate (struct n))
+ (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
+ (($ $primcall 'builtin-ref (name))
+ (emit-builtin-ref asm dst (constant name)))
+ (($ $primcall 'bv-u8-ref (bv idx))
+ (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s8-ref (bv idx))
+ (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u16-ref (bv idx))
+ (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s16-ref (bv idx))
+ (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u32-ref (bv idx val))
+ (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s32-ref (bv idx val))
+ (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u64-ref (bv idx val))
+ (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s64-ref (bv idx val))
+ (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-f32-ref (bv idx val))
+ (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-f64-ref (bv idx val))
+ (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall name args)
+ ;; FIXME: Inline all the cases.
+ (let ((inst (prim-instruction name)))
+ (emit-text asm `((,inst ,dst ,@(map slot args))))))))
+
+ (define (compile-effect label exp k)
+ (match exp
+ (($ $values ()) #f)
+ (($ $prompt escape? tag handler)
+ (match (intmap-ref cps handler)
+ (($ $kreceive ($ $arity req () rest () #f) khandler-body)
+ (let ((receive-args (gensym "handler"))
+ (nreq (length req))
+ (proc-slot (lookup-call-proc-slot label allocation)))
+ (emit-prompt asm (slot tag) escape? proc-slot receive-args)
+ (emit-br asm k)
+ (emit-label asm receive-args)
+ (unless (and rest (zero? nreq))
+ (emit-receive-values asm proc-slot (->bool rest) nreq))
+ (when (and rest
+ (match (intmap-ref cps khandler-body)
+ (($ $kargs names (_ ... rest))
+ (maybe-slot rest))))
+ (emit-bind-rest asm (+ proc-slot 1 nreq)))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves handler allocation))
+ (emit-reset-frame asm frame-size)
+ (emit-br asm khandler-body)))))
+ (($ $primcall 'cache-current-module! (sym scope))
+ (emit-cache-current-module! asm (slot sym) (constant scope)))
+ (($ $primcall 'free-set! (closure idx value))
+ (emit-free-set! asm (slot closure) (slot value) (constant idx)))
+ (($ $primcall 'box-set! (box value))
+ (emit-box-set! asm (slot box) (slot value)))
+ (($ $primcall 'struct-set! (struct index value))
+ (emit-struct-set! asm (slot struct) (slot index) (slot value)))
+ (($ $primcall 'struct-set!/immediate (struct index value))
+ (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
+ (($ $primcall 'vector-set! (vector index value))
+ (emit-vector-set! asm (slot vector) (slot index) (slot value)))
+ (($ $primcall 'vector-set!/immediate (vector index value))
+ (emit-vector-set!/immediate asm (slot vector) (constant index)
+ (slot value)))
+ (($ $primcall 'set-car! (pair value))
+ (emit-set-car! asm (slot pair) (slot value)))
+ (($ $primcall 'set-cdr! (pair value))
+ (emit-set-cdr! asm (slot pair) (slot value)))
+ (($ $primcall 'define! (sym value))
+ (emit-define! asm (slot sym) (slot value)))
+ (($ $primcall 'push-fluid (fluid val))
+ (emit-push-fluid asm (slot fluid) (slot val)))
+ (($ $primcall 'pop-fluid ())
+ (emit-pop-fluid asm))
+ (($ $primcall 'wind (winder unwinder))
+ (emit-wind asm (slot winder) (slot unwinder)))
+ (($ $primcall 'bv-u8-set! (bv idx val))
+ (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s8-set! (bv idx val))
+ (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u16-set! (bv idx val))
+ (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s16-set! (bv idx val))
+ (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u32-set! (bv idx val))
+ (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s32-set! (bv idx val))
+ (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u64-set! (bv idx val))
+ (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s64-set! (bv idx val))
+ (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-f32-set! (bv idx val))
+ (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-f64-set! (bv idx val))
+ (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'unwind ())
+ (emit-unwind asm))))
+
+ (define (compile-values label exp syms)
+ (match exp
+ (($ $values args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation)))))
+
+ (define (compile-test label exp kt kf next-label)
+ (define (unary op sym)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot sym) #t kf))
+ (else
+ (op asm (slot sym) #f kt)
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))))
+ (define (binary op a b)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot a) (slot b) #t kf))
+ (else
+ (op asm (slot a) (slot b) #f kt)
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))))
+ (match exp
+ (($ $values (sym))
+ (call-with-values (lambda ()
+ (lookup-maybe-constant-value sym allocation))
+ (lambda (has-const? val)
+ (if has-const?
+ (if val
+ (unless (eq? kt next-label)
+ (emit-br asm kt))
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))
+ (unary emit-br-if-true sym)))))
+ (($ $primcall 'null? (a)) (unary emit-br-if-null a))
+ (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
+ (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
+ (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
+ (($ $primcall 'char? (a)) (unary emit-br-if-char a))
+ (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
+ (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
+ (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
+ (($ $primcall 'string? (a)) (unary emit-br-if-string a))
+ (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
+ (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
+ (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
+ ;; Add more TC7 tests here. Keep in sync with
+ ;; *branching-primcall-arities* in (language cps primitives) and
+ ;; the set of macro-instructions in assembly.scm.
+ (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
+ (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
+ (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
+ (($ $primcall '< (a b)) (binary emit-br-if-< a b))
+ (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
+ (($ $primcall '= (a b)) (binary emit-br-if-= a b))
+ (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
+ (($ $primcall '> (a b)) (binary emit-br-if-< b a))
+ (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
+
+ (define (compile-trunc label k exp nreq rest-var)
+ (define (do-call proc args emit-call)
+ (let* ((proc-slot (lookup-call-proc-slot label allocation))
+ (nargs (1+ (length args)))
+ (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-call asm proc-slot nargs)
+ (emit-dead-slot-map asm proc-slot
+ (lookup-dead-slot-map label allocation))
+ (cond
+ ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
+ (match (lookup-parallel-moves k allocation)
+ ((((? (lambda (src) (= src (1+ proc-slot))) src)
+ . dst)) dst)
+ (_ #f)))
+ ;; The usual case: one required live return value, ignoring
+ ;; any additional values.
+ => (lambda (dst)
+ (emit-receive asm dst proc-slot frame-size)))
+ (else
+ (unless (and (zero? nreq) rest-var)
+ (emit-receive-values asm proc-slot (->bool rest-var) nreq))
+ (when (and rest-var (maybe-slot rest-var))
+ (emit-bind-rest asm (+ proc-slot 1 nreq)))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves k allocation))
+ (emit-reset-frame asm frame-size)))))
+ (match exp
+ (($ $call proc args)
+ (do-call proc args
+ (lambda (asm proc-slot nargs)
+ (emit-call asm proc-slot nargs))))
+ (($ $callk k proc args)
+ (do-call proc args
+ (lambda (asm proc-slot nargs)
+ (emit-call-label asm proc-slot nargs k))))))
+
+ (define (compile-expression label k exp)
+ (let* ((fallthrough? (= k (1+ label))))
+ (define (maybe-emit-jump)
+ (unless fallthrough?
+ (emit-br asm k)))
+ (match (intmap-ref cps k)
+ (($ $ktail)
+ (compile-tail label exp))
+ (($ $kargs (name) (sym))
+ (let ((dst (maybe-slot sym)))
+ (when dst
+ (compile-value label exp dst)))
+ (maybe-emit-jump))
+ (($ $kargs () ())
+ (match exp
+ (($ $branch kt exp)
+ (compile-test label exp kt k (1+ label)))
+ (_
+ (compile-effect label exp k)
+ (maybe-emit-jump))))
+ (($ $kargs names syms)
+ (compile-values label exp syms)
+ (maybe-emit-jump))
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (compile-trunc label k exp (length req)
+ (and rest
+ (match (intmap-ref cps kargs)
+ (($ $kargs names (_ ... rest)) rest))))
+ (unless (and fallthrough? (= kargs (1+ k)))
+ (emit-br asm kargs))))))
+
+ (define (compile-cont label cont)
+ (match cont
+ (($ $kfun src meta self tail clause)
+ (when src
+ (emit-source asm src))
+ (emit-begin-program asm label meta))
+ (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alt)
+ (let ((first? (match (intmap-ref cps (1- label))
+ (($ $kfun) #t)
+ (_ #f)))
+ (kw-indices (map (match-lambda
+ ((key name sym)
+ (cons key (lookup-slot sym allocation))))
+ kw)))
+ (unless first?
+ (emit-end-arity asm))
+ (emit-label asm label)
+ (set! frame-size (lookup-nlocals label allocation))
+ (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
+ frame-size alt)))
+ (($ $kargs names vars ($ $continue k src exp))
+ (emit-label asm label)
+ (for-each (lambda (name var)
+ (let ((slot (maybe-slot var)))
+ (when slot
+ (emit-definition asm name slot))))
+ names vars)
+ (when src
+ (emit-source asm src))
+ (compile-expression label k exp))
+ (($ $kreceive arity kargs)
+ (emit-label asm label))
+ (($ $ktail)
+ (emit-end-arity asm)
+ (emit-end-program asm))))
+
+ (intmap-for-each compile-cont cps)))
+
+(define (emit-bytecode exp env opts)
+ (let ((asm (make-assembler)))
+ (intmap-for-each (lambda (kfun body)
+ (compile-function (intmap-select exp body) asm))
+ (compute-reachable-functions exp 0))
+ (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
+ env
+ env)))
+
+(define (lower-cps exp opts)
+ (set! exp (optimize-higher-order-cps exp opts))
+ (set! exp (convert-closures exp))
+ (set! exp (optimize-first-order-cps exp opts))
+ (set! exp (reify-primitives exp))
+ (renumber exp))
+
+(define (compile-bytecode exp env opts)
+ (set! exp (lower-cps exp opts))
+ (emit-bytecode exp env opts))
diff --git a/module/language/cps2/slot-allocation.scm b/module/language/cps2/slot-allocation.scm
new file mode 100644
index 000000000..48f5a1fd3
--- /dev/null
+++ b/module/language/cps2/slot-allocation.scm
@@ -0,0 +1,995 @@
+;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A module to assign stack slots to variables in a CPS term.
+;;;
+;;; Code:
+
+(define-module (language cps2 slot-allocation)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-26)
+ #:use-module (language cps2)
+ #:use-module (language cps2 utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (allocate-slots
+ lookup-slot
+ lookup-maybe-slot
+ lookup-constant-value
+ lookup-maybe-constant-value
+ lookup-nlocals
+ lookup-call-proc-slot
+ lookup-parallel-moves
+ lookup-dead-slot-map))
+
+(define-record-type $allocation
+ (make-allocation slots constant-values call-allocs shuffles frame-sizes)
+ allocation?
+
+ ;; A map of VAR to slot allocation. A slot allocation is an integer,
+ ;; if the variable has been assigned a slot.
+ ;;
+ (slots allocation-slots)
+
+ ;; A map of VAR to constant value, for variables with constant values.
+ ;;
+ (constant-values allocation-constant-values)
+
+ ;; A map of LABEL to /call allocs/, for expressions that continue to
+ ;; $kreceive continuations: non-tail calls and $prompt expressions.
+ ;;
+ ;; A call alloc contains two pieces of information: the call's /proc
+ ;; slot/ and a /dead slot map/. The proc slot indicates the slot of a
+ ;; procedure in a procedure call, or where the procedure would be in a
+ ;; multiple-value return.
+ ;;
+ ;; The dead slot map indicates, what slots should be ignored by GC
+ ;; when marking the frame. A dead slot map is a bitfield, as an
+ ;; integer.
+ ;;
+ (call-allocs allocation-call-allocs)
+
+ ;; A map of LABEL to /parallel moves/. Parallel moves shuffle locals
+ ;; into position for a $call, $callk, or $values, or shuffle returned
+ ;; values back into place in a $kreceive.
+ ;;
+ ;; A set of moves is expressed as an ordered list of (SRC . DST)
+ ;; moves, where SRC and DST are slots. This may involve a temporary
+ ;; variable.
+ ;;
+ (shuffles allocation-shuffles)
+
+ ;; The number of locals for a $kclause.
+ ;;
+ (frame-sizes allocation-frame-sizes))
+
+(define-record-type $call-alloc
+ (make-call-alloc proc-slot dead-slot-map)
+ call-alloc?
+ (proc-slot call-alloc-proc-slot)
+ (dead-slot-map call-alloc-dead-slot-map))
+
+(define (lookup-maybe-slot var allocation)
+ (intmap-ref (allocation-slots allocation) var (lambda (_) #f)))
+
+(define (lookup-slot var allocation)
+ (intmap-ref (allocation-slots allocation) var))
+
+(define *absent* (list 'absent))
+
+(define (lookup-constant-value var allocation)
+ (let ((value (intmap-ref (allocation-constant-values allocation) var
+ (lambda (_) *absent*))))
+ (when (eq? value *absent*)
+ (error "Variable does not have constant value" var))
+ value))
+
+(define (lookup-maybe-constant-value var allocation)
+ (let ((value (intmap-ref (allocation-constant-values allocation) var
+ (lambda (_) *absent*))))
+ (if (eq? value *absent*)
+ (values #f #f)
+ (values #t value))))
+
+(define (lookup-call-alloc k allocation)
+ (intmap-ref (allocation-call-allocs allocation) k))
+
+(define (lookup-call-proc-slot k allocation)
+ (or (call-alloc-proc-slot (lookup-call-alloc k allocation))
+ (error "Call has no proc slot" k)))
+
+(define (lookup-parallel-moves k allocation)
+ (intmap-ref (allocation-shuffles allocation) k))
+
+(define (lookup-dead-slot-map k allocation)
+ (or (call-alloc-dead-slot-map (lookup-call-alloc k allocation))
+ (error "Call has no dead slot map" k)))
+
+(define (lookup-nlocals k allocation)
+ (intmap-ref (allocation-frame-sizes allocation) k))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define (solve-flow-equations succs in out kill gen subtract add meet)
+ "Find a fixed point for flow equations for SUCCS, where IN and OUT are
+the initial conditions as intmaps with one key for every node in SUCCS.
+KILL and GEN are intmaps indicating the state that is killed or defined
+at every node, and SUBTRACT, ADD, and MEET operates on that state."
+ (define (visit label in out)
+ (let* ((in-1 (intmap-ref in label))
+ (kill-1 (intmap-ref kill label))
+ (gen-1 (intmap-ref gen label))
+ (out-1 (intmap-ref out label))
+ (out-1* (add (subtract in-1 kill-1) gen-1)))
+ (if (eq? out-1 out-1*)
+ (values empty-intset in out)
+ (let ((out (intmap-replace! out label out-1*)))
+ (call-with-values
+ (lambda ()
+ (intset-fold (lambda (succ in changed)
+ (let* ((in-1 (intmap-ref in succ))
+ (in-1* (meet in-1 out-1*)))
+ (if (eq? in-1 in-1*)
+ (values in changed)
+ (values (intmap-replace! in succ in-1*)
+ (intset-add changed succ)))))
+ (intmap-ref succs label) in empty-intset))
+ (lambda (in changed)
+ (values changed in out)))))))
+
+ (let run ((worklist (intmap-keys succs)) (in in) (out out))
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist popped)
+ (if popped
+ (call-with-values (lambda () (visit popped in out))
+ (lambda (changed in out)
+ (run (intset-union worklist changed) in out)))
+ (values (persistent-intmap in)
+ (persistent-intmap out)))))))
+
+(define-syntax-rule (persistent-intmap2 exp)
+ (call-with-values (lambda () exp)
+ (lambda (a b)
+ (values (persistent-intmap a) (persistent-intmap b)))))
+
+(define (compute-defs-and-uses cps)
+ "Return two LABEL->VAR... maps indicating values defined at and used
+by a label, respectively."
+ (define (vars->intset vars)
+ (fold (lambda (var set) (intset-add set var)) empty-intset vars))
+ (persistent-intmap2
+ (intmap-fold
+ (lambda (label cont defs uses)
+ (define (get-defs k)
+ (match (intmap-ref cps k)
+ (($ $kargs names vars) (vars->intset vars))
+ (_ empty-intset)))
+ (define (return d u)
+ (values (intmap-add! defs label d)
+ (intmap-add! uses label u)))
+ (match cont
+ (($ $kfun src meta self)
+ (return (intset self) empty-intset))
+ (($ $kargs _ _ ($ $continue k src exp))
+ (match exp
+ ((or ($ $const) ($ $closure))
+ (return (get-defs k) empty-intset))
+ (($ $call proc args)
+ (return (get-defs k) (intset-add (vars->intset args) proc)))
+ (($ $callk _ proc args)
+ (return (get-defs k) (intset-add (vars->intset args) proc)))
+ (($ $primcall name args)
+ (return (get-defs k) (vars->intset args)))
+ (($ $branch kt ($ $primcall name args))
+ (return empty-intset (vars->intset args)))
+ (($ $branch kt ($ $values args))
+ (return empty-intset (vars->intset args)))
+ (($ $values args)
+ (return (get-defs k) (vars->intset args)))
+ (($ $prompt escape? tag handler)
+ (return empty-intset (intset tag)))))
+ (($ $kclause arity body alt)
+ (return (get-defs body) empty-intset))
+ (($ $kreceive arity kargs)
+ (return (get-defs kargs) empty-intset))
+ (($ $ktail)
+ (return empty-intset empty-intset))))
+ cps
+ empty-intmap
+ empty-intmap)))
+
+(define (compute-reverse-control-flow-order preds)
+ "Return a LABEL->ORDER bijection where ORDER is a contiguous set of
+integers starting from 0 and incrementing in sort order."
+ ;; This is more involved than forward control flow because not all
+ ;; live labels are reachable from the tail.
+ (persistent-intmap
+ (fold2 (lambda (component order n)
+ (intset-fold (lambda (label order n)
+ (values (intmap-add! order label n)
+ (1+ n)))
+ component order n))
+ (reverse (compute-sorted-strongly-connected-components preds))
+ empty-intmap 0)))
+
+(define* (add-prompt-control-flow-edges conts succs #:key complete?)
+ "For all prompts in DFG in the range [MIN-LABEL, MIN-LABEL +
+LABEL-COUNT), invoke F with arguments PROMPT, HANDLER, and BODY for each
+body continuation in the prompt."
+ (define (intset-filter pred set)
+ (intset-fold (lambda (i set)
+ (if (pred i) set (intset-remove set i)))
+ set
+ set))
+ (define (intset-any pred set)
+ (intset-fold (lambda (i res)
+ (if (or res (pred i)) #t res))
+ set
+ #f))
+ (define (visit-prompt label handler succs)
+ ;; FIXME: It isn't correct to use all continuations reachable from
+ ;; the prompt, because that includes continuations outside the
+ ;; prompt body. This point is moot if the handler's control flow
+ ;; joins with the the body, as is usually but not always the case.
+ ;;
+ ;; One counter-example is when the handler contifies an infinite
+ ;; loop; in that case we compute a too-large prompt body. This
+ ;; error is currently innocuous, but we should fix it at some point.
+ ;;
+ ;; The fix is to end the body at the corresponding "pop" primcall,
+ ;; if any.
+ (let ((body (intset-subtract (compute-function-body conts label)
+ (compute-function-body conts handler))))
+ (define (out-or-back-edge? label)
+ ;; Most uses of visit-prompt-control-flow don't need every body
+ ;; continuation, and would be happy getting called only for
+ ;; continuations that postdominate the rest of the body. Unless
+ ;; you pass #:complete? #t, we only invoke F on continuations
+ ;; that can leave the body, or on back-edges in loops.
+ ;;
+ ;; You would think that looking for the final "pop" primcall
+ ;; would be sufficient, but that is incorrect; it's possible for
+ ;; a loop in the prompt body to be contified, and that loop need
+ ;; not continue to the pop if it never terminates. The pop could
+ ;; even be removed by DCE, in that case.
+ (intset-any (lambda (succ)
+ (or (not (intset-ref body succ))
+ (<= succ label)))
+ (intmap-ref succs label)))
+ (intset-fold (lambda (pred succs)
+ (intmap-replace succs pred handler intset-add))
+ (if complete? body (intset-filter out-or-back-edge? body))
+ succs)))
+ (intmap-fold
+ (lambda (label cont succs)
+ (match cont
+ (($ $kargs _ _
+ ($ $continue _ _ ($ $prompt escape? tag handler)))
+ (visit-prompt label handler succs))
+ (_ succs)))
+ conts
+ succs))
+
+(define (rename-keys map old->new)
+ (persistent-intmap
+ (intmap-fold (lambda (k v out)
+ (intmap-add! out (intmap-ref old->new k) v))
+ map
+ empty-intmap)))
+
+(define (rename-intset set old->new)
+ (intset-fold (lambda (old set) (intset-add set (intmap-ref old->new old)))
+ set empty-intset))
+
+(define (rename-graph graph old->new)
+ (persistent-intmap
+ (intmap-fold (lambda (pred succs out)
+ (intmap-add! out
+ (intmap-ref old->new pred)
+ (rename-intset succs old->new)))
+ graph
+ empty-intmap)))
+
+(define (compute-live-variables cps defs uses)
+ "Compute and return two values mapping LABEL->VAR..., where VAR... are
+the definitions that are live before and after LABEL, as intsets."
+ (let* ((succs (add-prompt-control-flow-edges cps (compute-successors cps)))
+ (preds (invert-graph succs))
+ (old->new (compute-reverse-control-flow-order preds)))
+ (call-with-values
+ (lambda ()
+ (let ((init (rename-keys
+ (intmap-map (lambda (k v) empty-intset) preds)
+ old->new)))
+ (solve-flow-equations (rename-graph preds old->new)
+ init init
+ (rename-keys defs old->new)
+ (rename-keys uses old->new)
+ intset-subtract intset-union intset-union)))
+ (lambda (in out)
+ ;; As a reverse control-flow problem, the values flowing into a
+ ;; node are actually the live values after the node executes.
+ ;; Funny, innit? So we return them in the reverse order.
+ (let ((new->old (invert-bijection old->new)))
+ (values (rename-keys out new->old)
+ (rename-keys in new->old)))))))
+
+(define (compute-needs-slot cps defs uses)
+ (define (get-defs k) (intmap-ref defs k))
+ (define (get-uses label) (intmap-ref uses label))
+ (intmap-fold
+ (lambda (label cont needs-slot)
+ (intset-union
+ needs-slot
+ (match cont
+ (($ $kargs _ _ ($ $continue k src exp))
+ (let ((defs (get-defs label)))
+ (define (defs+* uses)
+ (intset-union defs uses))
+ (define (defs+ use)
+ (intset-add defs use))
+ (match exp
+ (($ $const)
+ empty-intset)
+ (($ $primcall 'free-ref (closure slot))
+ (defs+ closure))
+ (($ $primcall 'free-set! (closure slot value))
+ (defs+* (intset closure value)))
+ (($ $primcall 'cache-current-module! (mod . _))
+ (defs+ mod))
+ (($ $primcall 'cached-toplevel-box _)
+ defs)
+ (($ $primcall 'cached-module-box _)
+ defs)
+ (($ $primcall 'resolve (name bound?))
+ (defs+ name))
+ (($ $primcall 'make-vector/immediate (len init))
+ (defs+ init))
+ (($ $primcall 'vector-ref/immediate (v i))
+ (defs+ v))
+ (($ $primcall 'vector-set!/immediate (v i x))
+ (defs+* (intset v x)))
+ (($ $primcall 'allocate-struct/immediate (vtable nfields))
+ (defs+ vtable))
+ (($ $primcall 'struct-ref/immediate (s n))
+ (defs+ s))
+ (($ $primcall 'struct-set!/immediate (s n x))
+ (defs+* (intset s x)))
+ (($ $primcall 'builtin-ref (idx))
+ defs)
+ (_
+ (defs+* (get-uses label))))))
+ (($ $kreceive arity k)
+ ;; Only allocate results of function calls to slots if they are
+ ;; used.
+ empty-intset)
+ (($ $kclause arity body alternate)
+ (get-defs label))
+ (($ $kfun src meta self)
+ (intset self))
+ (($ $ktail)
+ empty-intset))))
+ cps
+ empty-intset))
+
+(define (compute-lazy-vars cps live-in live-out defs needs-slot)
+ "Compute and return a set of vars whose allocation can be delayed
+until their use is seen. These are \"lazy\" vars. A var is lazy if its
+uses are calls, it is always dead after the calls, and if the uses flow
+to the definition. A flow continues across a node iff the node kills no
+values that need slots, and defines only lazy vars. Calls also kill
+flows; there's no sense in trying to juggle a pending frame while there
+is an active call."
+ (define (list->intset list)
+ (persistent-intset
+ (fold (lambda (i set) (intset-add! set i)) empty-intset list)))
+
+ (let* ((succs (compute-successors cps))
+ (gens (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ ($ $call proc args)))
+ (intset-subtract (intset-add (list->intset args) proc)
+ (intmap-ref live-out label)))
+ (($ $kargs _ _ ($ $continue _ _ ($ $callk _ proc args)))
+ (intset-subtract (intset-add (list->intset args) proc)
+ (intmap-ref live-out label)))
+ (_ #f)))
+ cps))
+ (kills (intmap-map
+ (lambda (label in)
+ (let* ((out (intmap-ref live-out label))
+ (killed (intset-subtract in out))
+ (killed-slots (intset-intersect killed needs-slot)))
+ (and (eq? killed-slots empty-intset)
+ ;; Kill output variables that need slots.
+ (intset-intersect (intmap-ref defs label)
+ needs-slot))))
+ live-in))
+ (preds (invert-graph succs))
+ (old->new (compute-reverse-control-flow-order preds)))
+ (define (subtract lazy kill)
+ (cond
+ ((eq? lazy empty-intset)
+ lazy)
+ ((not kill)
+ empty-intset)
+ ((and lazy (eq? empty-intset (intset-subtract kill lazy)))
+ (intset-subtract lazy kill))
+ (else
+ empty-intset)))
+ (define (add live gen) (or gen live))
+ (define (meet in out)
+ ;; Initial in is #f.
+ (if in (intset-intersect in out) out))
+ (call-with-values
+ (lambda ()
+ (let ((succs (rename-graph preds old->new))
+ (in (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
+ (out (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
+ ;(out (rename-keys gens old->new))
+ (kills (rename-keys kills old->new))
+ (gens (rename-keys gens old->new)))
+ (solve-flow-equations succs in out kills gens subtract add meet)))
+ (lambda (in out)
+ ;; A variable is lazy if its uses reach its definition.
+ (intmap-fold (lambda (label out lazy)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars)
+ (let ((defs (list->intset vars)))
+ (intset-union lazy (intset-intersect out defs))))
+ (_ lazy)))
+ (rename-keys out (invert-bijection old->new))
+ empty-intset)))))
+
+(define (find-first-zero n)
+ ;; Naive implementation.
+ (let lp ((slot 0))
+ (if (logbit? slot n)
+ (lp (1+ slot))
+ slot)))
+
+(define (find-first-trailing-zero n)
+ (let lp ((slot (let lp ((count 2))
+ (if (< n (ash 1 (1- count)))
+ count
+ ;; Grow upper bound slower than factor 2 to avoid
+ ;; needless bignum allocation on 32-bit systems
+ ;; when there are more than 16 locals.
+ (lp (+ count (ash count -1)))))))
+ (if (or (zero? slot) (logbit? (1- slot) n))
+ slot
+ (lp (1- slot)))))
+
+(define (integers from count)
+ (if (zero? count)
+ '()
+ (cons from (integers (1+ from) (1- count)))))
+
+(define (solve-parallel-move src dst tmp)
+ "Solve the parallel move problem between src and dst slot lists, which
+are comparable with eqv?. A tmp slot may be used."
+
+ ;; This algorithm is taken from: "Tilting at windmills with Coq:
+ ;; formal verification of a compilation algorithm for parallel moves"
+ ;; by Laurence Rideau, Bernard Paul Serpette, and Xavier Leroy
+ ;;
+
+ (define (split-move moves reg)
+ (let loop ((revhead '()) (tail moves))
+ (match tail
+ (((and s+d (s . d)) . rest)
+ (if (eqv? s reg)
+ (cons d (append-reverse revhead rest))
+ (loop (cons s+d revhead) rest)))
+ (_ #f))))
+
+ (define (replace-last-source reg moves)
+ (match moves
+ ((moves ... (s . d))
+ (append moves (list (cons reg d))))))
+
+ (let loop ((to-move (map cons src dst))
+ (being-moved '())
+ (moved '())
+ (last-source #f))
+ ;; 'last-source' should always be equivalent to:
+ ;; (and (pair? being-moved) (car (last being-moved)))
+ (match being-moved
+ (() (match to-move
+ (() (reverse moved))
+ (((and s+d (s . d)) . t1)
+ (if (or (eqv? s d) ; idempotent
+ (not s)) ; src is a constant and can be loaded directly
+ (loop t1 '() moved #f)
+ (loop t1 (list s+d) moved s)))))
+ (((and s+d (s . d)) . b)
+ (match (split-move to-move d)
+ ((r . t1) (loop t1 (acons d r being-moved) moved last-source))
+ (#f (match b
+ (() (loop to-move '() (cons s+d moved) #f))
+ (_ (if (eqv? d last-source)
+ (loop to-move
+ (replace-last-source tmp b)
+ (cons s+d (acons d tmp moved))
+ tmp)
+ (loop to-move b (cons s+d moved) last-source))))))))))
+
+(define (compute-shuffles cps slots call-allocs live-in)
+ (define (add-live-slot slot live-slots)
+ (logior live-slots (ash 1 slot)))
+
+ (define (get-cont label)
+ (intmap-ref cps label))
+
+ (define (get-slot var)
+ (intmap-ref slots var (lambda (_) #f)))
+
+ (define (get-slots vars)
+ (let lp ((vars vars))
+ (match vars
+ ((var . vars) (cons (get-slot var) (lp vars)))
+ (_ '()))))
+
+ (define (get-proc-slot label)
+ (call-alloc-proc-slot (intmap-ref call-allocs label)))
+
+ (define (compute-live-slots label)
+ (intset-fold (lambda (var live)
+ (match (get-slot var)
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-in label)
+ 0))
+
+ ;; Although some parallel moves may proceed without a temporary slot,
+ ;; in general one is needed. That temporary slot must not be part of
+ ;; the source or destination sets, and that slot should not correspond
+ ;; to a live variable. Usually the source and destination sets are a
+ ;; subset of the union of the live sets before and after the move.
+ ;; However for stack slots that don't have names -- those slots that
+ ;; correspond to function arguments or to function return values -- it
+ ;; could be that they are out of the computed live set. In that case
+ ;; they need to be adjoined to the live set, used when choosing a
+ ;; temporary slot.
+ ;;
+ ;; Note that although we reserve slots 253-255 for shuffling operands
+ ;; that address less than the full 24-bit range of locals, that
+ ;; reservation doesn't apply here, because this temporary itself is
+ ;; used while doing parallel assignment via "mov", and "mov" does not
+ ;; need shuffling.
+ (define (compute-tmp-slot live stack-slots)
+ (find-first-zero (fold add-live-slot live stack-slots)))
+
+ (define (parallel-move src-slots dst-slots tmp-slot)
+ (solve-parallel-move src-slots dst-slots tmp-slot))
+
+ (define (compute-receive-shuffles label proc-slot)
+ (match (get-cont label)
+ (($ $kreceive arity kargs)
+ (let* ((results (match (get-cont kargs)
+ (($ $kargs names vars) vars)))
+ (value-slots (integers (1+ proc-slot) (length results)))
+ (result-slots (get-slots results))
+ ;; Filter out unused results.
+ (value-slots (filter-map (lambda (val result) (and result val))
+ value-slots result-slots))
+ (result-slots (filter (lambda (x) x) result-slots))
+ (live (compute-live-slots kargs)))
+ (parallel-move value-slots
+ result-slots
+ (compute-tmp-slot live value-slots))))))
+
+ (define (add-call-shuffles label k args shuffles)
+ (match (get-cont k)
+ (($ $ktail)
+ (let* ((live (compute-live-slots label))
+ (tail-slots (integers 0 (length args)))
+ (moves (parallel-move (get-slots args)
+ tail-slots
+ (compute-tmp-slot live tail-slots))))
+ (intmap-add! shuffles label moves)))
+ (($ $kreceive)
+ (let* ((live (compute-live-slots label))
+ (proc-slot (get-proc-slot label))
+ (call-slots (integers proc-slot (length args)))
+ (arg-moves (parallel-move (get-slots args)
+ call-slots
+ (compute-tmp-slot live call-slots))))
+ (intmap-add! (intmap-add! shuffles label arg-moves)
+ k (compute-receive-shuffles k proc-slot))))))
+
+ (define (add-values-shuffles label k args shuffles)
+ (match (get-cont k)
+ (($ $ktail)
+ (let* ((live (compute-live-slots label))
+ (src-slots (get-slots args))
+ (dst-slots (integers 1 (length args)))
+ (moves (parallel-move src-slots dst-slots
+ (compute-tmp-slot live dst-slots))))
+ (intmap-add! shuffles label moves)))
+ (($ $kargs _ dst-vars)
+ (let* ((live (logior (compute-live-slots label)
+ (compute-live-slots k)))
+ (src-slots (get-slots args))
+ (dst-slots (get-slots dst-vars))
+ (moves (parallel-move src-slots dst-slots
+ (compute-tmp-slot live '()))))
+ (intmap-add! shuffles label moves)))))
+
+ (define (add-prompt-shuffles label k handler shuffles)
+ (intmap-add! shuffles handler
+ (compute-receive-shuffles handler (get-proc-slot label))))
+
+ (define (compute-shuffles label cont shuffles)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $call proc args)
+ (add-call-shuffles label k (cons proc args) shuffles))
+ (($ $callk _ proc args)
+ (add-call-shuffles label k (cons proc args) shuffles))
+ (($ $values args)
+ (add-values-shuffles label k args shuffles))
+ (($ $prompt escape? tag handler)
+ (add-prompt-shuffles label k handler shuffles))
+ (_ shuffles)))
+ (_ shuffles)))
+
+ (persistent-intmap
+ (intmap-fold compute-shuffles cps empty-intmap)))
+
+(define (compute-frame-sizes cps slots call-allocs shuffles)
+ ;; Minimum frame has one slot: the closure.
+ (define minimum-frame-size 1)
+ (define (get-shuffles label)
+ (intmap-ref shuffles label))
+ (define (get-proc-slot label)
+ (match (intmap-ref call-allocs label (lambda (_) #f))
+ (#f 0) ;; Tail call.
+ (($ $call-alloc proc-slot) proc-slot)))
+ (define (max-size var size)
+ (match (intmap-ref slots var (lambda (_) #f))
+ (#f size)
+ (slot (max size (1+ slot)))))
+ (define (max-size* vars size)
+ (fold max-size size vars))
+ (define (shuffle-size moves size)
+ (match moves
+ (() size)
+ (((src . dst) . moves)
+ (shuffle-size moves (max size (1+ src) (1+ dst))))))
+ (define (call-size label nargs size)
+ (shuffle-size (get-shuffles label)
+ (max (+ (get-proc-slot label) nargs) size)))
+ (define (measure-cont label cont frame-sizes clause size)
+ (match cont
+ (($ $kfun)
+ (values #f #f #f))
+ (($ $kclause)
+ (let ((frame-sizes (if clause
+ (intmap-add! frame-sizes clause size)
+ empty-intmap)))
+ (values frame-sizes label minimum-frame-size)))
+ (($ $kargs names vars ($ $continue k src exp))
+ (values frame-sizes clause
+ (let ((size (max-size* vars size)))
+ (match exp
+ (($ $call proc args)
+ (call-size label (1+ (length args)) size))
+ (($ $callk _ proc args)
+ (call-size label (1+ (length args)) size))
+ (($ $values args)
+ (shuffle-size (get-shuffles label) size))
+ (_ size)))))
+ (($ $kreceive)
+ (values frame-sizes clause
+ (shuffle-size (get-shuffles label) size)))
+ (($ $ktail)
+ (values (intmap-add! frame-sizes clause size) #f #f))))
+
+ (persistent-intmap (intmap-fold measure-cont cps #f #f #f)))
+
+(define (allocate-args cps)
+ (intmap-fold (lambda (label cont slots)
+ (match cont
+ (($ $kfun src meta self)
+ (intmap-add! slots self 0))
+ (($ $kclause arity body alt)
+ (match (intmap-ref cps body)
+ (($ $kargs names vars)
+ (let lp ((vars vars) (slots slots) (n 1))
+ (match vars
+ (() slots)
+ ((var . vars)
+ (let ((n (if (<= 253 n 255) 256 n)))
+ (lp vars
+ (intmap-add! slots var n)
+ (1+ n)))))))))
+ (_ slots)))
+ cps empty-intmap))
+
+(define-inlinable (add-live-slot slot live-slots)
+ (logior live-slots (ash 1 slot)))
+
+(define-inlinable (kill-dead-slot slot live-slots)
+ (logand live-slots (lognot (ash 1 slot))))
+
+(define-inlinable (compute-slot live-slots hint)
+ ;; Slots 253-255 are reserved for shuffling; see comments in
+ ;; assembler.scm.
+ (if (and hint (not (logbit? hint live-slots))
+ (or (< hint 253) (> hint 255)))
+ hint
+ (let ((slot (find-first-zero live-slots)))
+ (if (or (< slot 253) (> slot 255))
+ slot
+ (+ 256 (find-first-zero (ash live-slots -256)))))))
+
+(define (allocate-lazy-vars cps slots call-allocs live-in lazy)
+ (define (compute-live-slots slots label)
+ (intset-fold (lambda (var live)
+ (match (intmap-ref slots var (lambda (_) #f))
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-in label)
+ 0))
+
+ (define (allocate var hint slots live)
+ (match (and hint (intmap-ref slots var (lambda (_) #f)))
+ (#f (if (intset-ref lazy var)
+ (let ((slot (compute-slot live hint)))
+ (values (intmap-add! slots var slot)
+ (add-live-slot slot live)))
+ (values slots live)))
+ (slot (values slots (add-live-slot slot live)))))
+
+ (define (allocate* vars hints slots live)
+ (match (vector vars hints)
+ (#(() ()) slots)
+ (#((var . vars) (hint . hints))
+ (let-values (((slots live) (allocate var hint slots live)))
+ (allocate* vars hints slots live)))))
+
+ (define (get-proc-slot label)
+ (match (intmap-ref call-allocs label (lambda (_) #f))
+ (#f 0)
+ (call (call-alloc-proc-slot call))))
+
+ (define (allocate-call label args slots)
+ (allocate* args (integers (get-proc-slot label) (length args))
+ slots (compute-live-slots slots label)))
+
+ (define (allocate-values label k args slots)
+ (match (intmap-ref cps k)
+ (($ $ktail)
+ (allocate* args (integers 1 (length args))
+ slots (compute-live-slots slots label)))
+ (($ $kargs names vars)
+ (allocate* args
+ (map (cut intmap-ref slots <> (lambda (_) #f)) vars)
+ slots (compute-live-slots slots label)))))
+
+ (define (allocate-lazy label cont slots)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $call proc args)
+ (allocate-call label (cons proc args) slots))
+ (($ $callk _ proc args)
+ (allocate-call label (cons proc args) slots))
+ (($ $values args)
+ (allocate-values label k args slots))
+ (_ slots)))
+ (_
+ slots)))
+
+ ;; Sweep right to left to visit uses before definitions.
+ (persistent-intmap
+ (intmap-fold-right allocate-lazy cps slots)))
+
+(define (allocate-slots cps)
+ (let*-values (((defs uses) (compute-defs-and-uses cps))
+ ((live-in live-out) (compute-live-variables cps defs uses))
+ ((constants) (compute-constant-values cps))
+ ((needs-slot) (compute-needs-slot cps defs uses))
+ ((lazy) (compute-lazy-vars cps live-in live-out defs
+ needs-slot)))
+
+ (define (empty-live-slots)
+ #b0)
+
+ (define (compute-call-proc-slot live-slots)
+ (+ 2 (find-first-trailing-zero live-slots)))
+
+ (define (compute-prompt-handler-proc-slot live-slots)
+ (if (zero? live-slots)
+ 0
+ (1- (find-first-trailing-zero live-slots))))
+
+ (define (get-cont label)
+ (intmap-ref cps label))
+
+ (define (get-slot slots var)
+ (intmap-ref slots var (lambda (_) #f)))
+
+ (define (get-slots slots vars)
+ (let lp ((vars vars))
+ (match vars
+ ((var . vars) (cons (get-slot slots var) (lp vars)))
+ (_ '()))))
+
+ (define (compute-live-slots* slots label live-vars)
+ (intset-fold (lambda (var live)
+ (match (get-slot slots var)
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-vars label)
+ 0))
+
+ (define (compute-live-in-slots slots label)
+ (compute-live-slots* slots label live-in))
+
+ (define (compute-live-out-slots slots label)
+ (compute-live-slots* slots label live-out))
+
+ (define (allocate var hint slots live)
+ (cond
+ ((not (intset-ref needs-slot var))
+ (values slots live))
+ ((get-slot slots var)
+ => (lambda (slot)
+ (values slots (add-live-slot slot live))))
+ ((and (not hint) (intset-ref lazy var))
+ (values slots live))
+ (else
+ (let ((slot (compute-slot live hint)))
+ (values (intmap-add! slots var slot)
+ (add-live-slot slot live))))))
+
+ (define (allocate* vars hints slots live)
+ (match (vector vars hints)
+ (#(() ()) (values slots live))
+ (#((var . vars) (hint . hints))
+ (call-with-values (lambda () (allocate var hint slots live))
+ (lambda (slots live)
+ (allocate* vars hints slots live))))))
+
+ (define (allocate-defs label vars slots)
+ (let ((live (compute-live-in-slots slots label))
+ (live-vars (intmap-ref live-in label)))
+ (let lp ((vars vars) (slots slots) (live live))
+ (match vars
+ (() (values slots live))
+ ((var . vars)
+ (call-with-values (lambda () (allocate var #f slots live))
+ (lambda (slots live)
+ (lp vars slots
+ (let ((slot (get-slot slots var)))
+ (if (and slot (not (intset-ref live-vars var)))
+ (kill-dead-slot slot live)
+ live))))))))))
+
+ ;; PRE-LIVE are the live slots coming into the term. POST-LIVE
+ ;; is the subset of PRE-LIVE that is still live after the term
+ ;; uses its inputs.
+ (define (allocate-call label k args slots call-allocs pre-live)
+ (match (get-cont k)
+ (($ $ktail)
+ (let ((tail-slots (integers 0 (length args))))
+ (values (allocate* args tail-slots slots pre-live)
+ call-allocs)))
+ (($ $kreceive arity kargs)
+ (let*-values
+ (((post-live) (compute-live-out-slots slots label))
+ ((proc-slot) (compute-call-proc-slot post-live))
+ ((call-slots) (integers proc-slot (length args)))
+ ((slots pre-live) (allocate* args call-slots slots pre-live))
+ ;; Allow the first result to be hinted by its use, but
+ ;; hint the remaining results to stay in place. This
+ ;; strikes a balance between avoiding shuffling,
+ ;; especially for unused extra values, and avoiding frame
+ ;; size growth due to sparse locals.
+ ((slots result-live)
+ (match (get-cont kargs)
+ (($ $kargs () ())
+ (values slots post-live))
+ (($ $kargs (_ . _) (_ . results))
+ (let ((result-slots (integers (+ proc-slot 2)
+ (length results))))
+ (allocate* results result-slots slots post-live)))))
+ ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
+ (lognot post-live)))
+ ((call) (make-call-alloc proc-slot dead-slot-map)))
+ (values slots
+ (intmap-add! call-allocs label call))))))
+
+ (define (allocate-values label k args slots call-allocs)
+ (match (get-cont k)
+ (($ $ktail)
+ (values slots call-allocs))
+ (($ $kargs (_) (dst))
+ ;; When there is only one value in play, we allow the dst to be
+ ;; hinted (see compute-lazy-vars). If the src doesn't have a
+ ;; slot, then the actual slot for the dst would end up being
+ ;; decided by the call that args it. Because we don't know the
+ ;; slot, we can't really compute the parallel moves in that
+ ;; case, so just bail and rely on the bytecode emitter to
+ ;; handle the one-value case specially.
+ (match args
+ ((src)
+ (let ((post-live (compute-live-out-slots slots label)))
+ (values (allocate dst (get-slot slots src) slots post-live)
+ call-allocs)))))
+ (($ $kargs _ dst-vars)
+ (let ((src-slots (get-slots slots args))
+ (post-live (compute-live-out-slots slots label)))
+ (values (allocate* dst-vars src-slots slots post-live)
+ call-allocs)))))
+
+ (define (allocate-prompt label k handler slots call-allocs)
+ (match (get-cont handler)
+ (($ $kreceive arity kargs)
+ (let*-values
+ (((handler-live) (compute-live-in-slots slots handler))
+ ((proc-slot) (compute-prompt-handler-proc-slot handler-live))
+ ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
+ (lognot handler-live)))
+ ((result-vars) (match (get-cont kargs)
+ (($ $kargs names vars) vars)))
+ ((value-slots) (integers (1+ proc-slot) (length result-vars)))
+ ((slots result-live) (allocate* result-vars value-slots
+ slots handler-live)))
+ (values slots
+ (intmap-add! call-allocs label
+ (make-call-alloc proc-slot dead-slot-map)))))))
+
+ (define (allocate-cont label cont slots call-allocs)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (let-values (((slots live) (allocate-defs label vars slots)))
+ (match exp
+ (($ $call proc args)
+ (allocate-call label k (cons proc args) slots call-allocs live))
+ (($ $callk _ proc args)
+ (allocate-call label k (cons proc args) slots call-allocs live))
+ (($ $values args)
+ (allocate-values label k args slots call-allocs))
+ (($ $prompt escape? tag handler)
+ (allocate-prompt label k handler slots call-allocs))
+ (_
+ (values slots call-allocs)))))
+ (_
+ (values slots call-allocs))))
+
+ (call-with-values (lambda ()
+ (let ((slots (allocate-args cps)))
+ (intmap-fold allocate-cont cps slots empty-intmap)))
+ (lambda (slots calls)
+ (let* ((slots (allocate-lazy-vars cps slots calls live-in lazy))
+ (shuffles (compute-shuffles cps slots calls live-in))
+ (frame-sizes (compute-frame-sizes cps slots calls shuffles)))
+ (make-allocation slots constants calls shuffles frame-sizes))))))
From 39777b11b379ce103dcefe01adba3a6d480c574f Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 17:01:53 +0200
Subject: [PATCH 030/865] Compile CPS2 directly to bytecode
* module/language/cps2/spec.scm (cps2): Compile directly to bytecode.
---
module/language/cps2/spec.scm | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/module/language/cps2/spec.scm b/module/language/cps2/spec.scm
index 5ab30ff6b..ac8f06439 100644
--- a/module/language/cps2/spec.scm
+++ b/module/language/cps2/spec.scm
@@ -21,7 +21,7 @@
(define-module (language cps2 spec)
#:use-module (system base language)
#:use-module (language cps2)
- #:use-module (language cps2 compile-cps)
+ #:use-module (language cps2 compile-bytecode)
#:export (cps2))
(define* (write-cps exp #:optional (port (current-output-port)))
@@ -32,6 +32,6 @@
#:reader (lambda (port env) (read port))
#:printer write-cps
#:parser parse-cps
- #:compilers `((cps . ,compile-cps))
+ #:compilers `((bytecode . ,compile-bytecode))
#:for-humans? #f
)
From 0d4c9377222ebb45c673b413c0a1f7abd993f8ed Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 17:15:06 +0200
Subject: [PATCH 031/865] Remove CPS1 language
* module/language/cps.scm:
* module/language/cps/compile-bytecode.scm:
* module/language/cps/dfg.scm:
* module/language/cps/renumber.scm:
* module/language/cps/slot-allocation.scm:
* module/language/cps/spec.scm:
* module/language/cps/verify.scm:
* module/language/cps2/compile-cps.scm: Delete.
* module/Makefile.am: Remove deleted files.
---
module/Makefile.am | 10 +-
module/language/cps.scm | 620 ----------------
module/language/cps/compile-bytecode.scm | 453 ------------
module/language/cps/dfg.scm | 904 -----------------------
module/language/cps/renumber.scm | 343 ---------
module/language/cps/slot-allocation.scm | 689 -----------------
module/language/cps/spec.scm | 37 -
module/language/cps/verify.scm | 195 -----
module/language/cps2/compile-cps.scm | 129 ----
9 files changed, 1 insertion(+), 3379 deletions(-)
delete mode 100644 module/language/cps.scm
delete mode 100644 module/language/cps/compile-bytecode.scm
delete mode 100644 module/language/cps/dfg.scm
delete mode 100644 module/language/cps/renumber.scm
delete mode 100644 module/language/cps/slot-allocation.scm
delete mode 100644 module/language/cps/spec.scm
delete mode 100644 module/language/cps/verify.scm
delete mode 100644 module/language/cps2/compile-cps.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 801f466cb..c53f9e466 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -122,20 +122,12 @@ TREE_IL_LANG_SOURCES = \
language/tree-il/spec.scm
CPS_LANG_SOURCES = \
- language/cps.scm \
- language/cps/compile-bytecode.scm \
- language/cps/dfg.scm \
- language/cps/primitives.scm \
- language/cps/renumber.scm \
- language/cps/slot-allocation.scm \
- language/cps/spec.scm \
- language/cps/verify.scm
+ language/cps/primitives.scm
CPS2_LANG_SOURCES = \
language/cps2.scm \
language/cps2/closure-conversion.scm \
language/cps2/compile-bytecode.scm \
- language/cps2/compile-cps.scm \
language/cps2/constructors.scm \
language/cps2/contification.scm \
language/cps2/cse.scm \
diff --git a/module/language/cps.scm b/module/language/cps.scm
deleted file mode 100644
index befa20f66..000000000
--- a/module/language/cps.scm
+++ /dev/null
@@ -1,620 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; This is the continuation-passing style (CPS) intermediate language
-;;; (IL) for Guile.
-;;;
-;;; There are two kinds of terms in CPS: terms that bind continuations,
-;;; and terms that call continuations.
-;;;
-;;; $letk binds a set of mutually recursive continuations, each one an
-;;; instance of $cont. A $cont declares the name of a continuation, and
-;;; then contains as a subterm the particular continuation instance:
-;;; $kargs for continuations that bind values, $ktail for the tail
-;;; continuation, etc.
-;;;
-;;; $continue nodes call continuations. The expression contained in the
-;;; $continue node determines the value or values that are passed to the
-;;; target continuation: $const to pass a constant value, $values to
-;;; pass multiple named values, etc. $continue nodes also record the source at which
-;;;
-;;; Additionally there is $letrec, a term that binds mutually recursive
-;;; functions. The contification pass will turn $letrec into $letk if
-;;; it can do so. Otherwise, the closure conversion pass will desugar
-;;; $letrec into an equivalent sequence of make-closure primcalls and
-;;; subsequent initializations of the captured variables of the
-;;; closures. You can think of $letrec as pertaining to "high CPS",
-;;; whereas later passes will only see "low CPS", which does not have
-;;; $letrec.
-;;;
-;;; This particular formulation of CPS was inspired by Andrew Kennedy's
-;;; 2007 paper, "Compiling with Continuations, Continued". All Guile
-;;; hackers should read that excellent paper! As in Kennedy's paper,
-;;; continuations are second-class, and may be thought of as basic block
-;;; labels. All values are bound to variables using continuation calls:
-;;; even constants!
-;;;
-;;; There are some Guile-specific quirks as well:
-;;;
-;;; - $kreceive represents a continuation that receives multiple values,
-;;; but which truncates them to some number of required values,
-;;; possibly with a rest list.
-;;;
-;;; - $kfun labels an entry point for a $fun (a function), and
-;;; contains a $ktail representing the formal argument which is the
-;;; function's continuation.
-;;;
-;;; - $kfun also contain a $kclause continuation, corresponding to
-;;; the first case-lambda clause of the function. $kclause actually
-;;; contains the clause body, and the subsequent clause (if any).
-;;; This is because the $kclause logically matches or doesn't match
-;;; a given set of actual arguments against a formal arity, then
-;;; proceeds to a "body" continuation (which is a $kargs).
-;;;
-;;; That's to say that a $fun can be matched like this:
-;;;
-;;; (match f
-;;; (($ $fun
-;;; ($ $cont kfun
-;;; ($ $kfun src meta self ($ $cont ktail ($ $ktail))
-;;; ($ $kclause arity
-;;; ($ $cont kbody ($ $kargs names syms body))
-;;; alternate))))
-;;; #t))
-;;;
-;;; A $continue to ktail is in tail position. $kfun, $kclause,
-;;; and $ktail will never be seen elsewhere in a CPS term.
-;;;
-;;; - $prompt continues to the body of the prompt, having pushed on a
-;;; prompt whose handler will continue at its "handler"
-;;; continuation. The continuation of the prompt is responsible for
-;;; popping the prompt.
-;;;
-;;; In summary:
-;;;
-;;; - $letk, $letrec, and $continue are terms.
-;;;
-;;; - $cont is a continuation, containing a continuation body ($kargs,
-;;; $ktail, etc).
-;;;
-;;; - $continue terms contain an expression ($call, $const, $fun,
-;;; etc).
-;;;
-;;; See (language tree-il compile-cps) for details on how Tree-IL
-;;; converts to CPS.
-;;;
-;;; Code:
-
-(define-module (language cps)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold))
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-9 gnu)
- #:use-module (srfi srfi-11)
- #:export (;; Helper.
- $arity
- make-$arity
-
- ;; Terms.
- $letk $continue
-
- ;; Continuations.
- $cont
-
- ;; Continuation bodies.
- $kreceive $kargs $kfun $ktail $kclause
-
- ;; Expressions.
- $const $prim $fun $rec $closure $branch
- $call $callk $primcall $values $prompt
-
- ;; First-order CPS root.
- $program
-
- ;; Fresh names.
- label-counter var-counter
- fresh-label fresh-var
- with-fresh-name-state compute-max-label-and-var
- let-fresh
-
- ;; Building macros.
- build-cps-term build-cps-cont build-cps-exp
- rewrite-cps-term rewrite-cps-cont rewrite-cps-exp
-
- ;; Misc.
- parse-cps unparse-cps
- make-global-cont-folder make-local-cont-folder
- fold-conts fold-local-conts
- visit-cont-successors))
-
-;; FIXME: Use SRFI-99, when Guile adds it.
-(define-syntax define-record-type*
- (lambda (x)
- (define (id-append ctx . syms)
- (datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
- (syntax-case x ()
- ((_ name field ...)
- (and (identifier? #'name) (and-map identifier? #'(field ...)))
- (with-syntax ((cons (id-append #'name #'make- #'name))
- (pred (id-append #'name #'name #'?))
- ((getter ...) (map (lambda (f)
- (id-append f #'name #'- f))
- #'(field ...))))
- #'(define-record-type name
- (cons field ...)
- pred
- (field getter)
- ...))))))
-
-(define-syntax-rule (define-cps-type name field ...)
- (begin
- (define-record-type* name field ...)
- (set-record-type-printer! name print-cps)))
-
-(define (print-cps exp port)
- (format port "#" (unparse-cps exp)))
-
-;; Helper.
-(define-record-type* $arity req opt rest kw allow-other-keys?)
-
-;; Terms.
-(define-cps-type $letk conts body)
-(define-cps-type $continue k src exp)
-
-;; Continuations
-(define-cps-type $cont k cont)
-(define-cps-type $kreceive arity k)
-(define-cps-type $kargs names syms body)
-(define-cps-type $kfun src meta self tail clause)
-(define-cps-type $ktail)
-(define-cps-type $kclause arity cont alternate)
-
-;; Expressions.
-(define-cps-type $const val)
-(define-cps-type $prim name)
-(define-cps-type $fun body) ; Higher-order.
-(define-cps-type $rec names syms funs) ; Higher-order.
-(define-cps-type $closure label nfree) ; First-order.
-(define-cps-type $branch k exp)
-(define-cps-type $call proc args)
-(define-cps-type $callk k proc args) ; First-order.
-(define-cps-type $primcall name args)
-(define-cps-type $values args)
-(define-cps-type $prompt escape? tag handler)
-
-;; The root of a higher-order CPS term is $cont containing a $kfun. The
-;; root of a first-order CPS term is a $program.
-(define-cps-type $program funs)
-
-(define label-counter (make-parameter #f))
-(define var-counter (make-parameter #f))
-
-(define (fresh-label)
- (let ((count (or (label-counter)
- (error "fresh-label outside with-fresh-name-state"))))
- (label-counter (1+ count))
- count))
-
-(define (fresh-var)
- (let ((count (or (var-counter)
- (error "fresh-var outside with-fresh-name-state"))))
- (var-counter (1+ count))
- count))
-
-(define-syntax-rule (let-fresh (label ...) (var ...) body ...)
- (let ((label (fresh-label)) ...
- (var (fresh-var)) ...)
- body ...))
-
-(define-syntax-rule (with-fresh-name-state fun body ...)
- (call-with-values (lambda () (compute-max-label-and-var fun))
- (lambda (max-label max-var)
- (parameterize ((label-counter (1+ max-label))
- (var-counter (1+ max-var)))
- body ...))))
-
-(define-syntax build-arity
- (syntax-rules (unquote)
- ((_ (unquote exp)) exp)
- ((_ (req opt rest kw allow-other-keys?))
- (make-$arity req opt rest kw allow-other-keys?))))
-
-(define-syntax build-cont-body
- (syntax-rules (unquote $kreceive $kargs $kfun $ktail $kclause)
- ((_ (unquote exp))
- exp)
- ((_ ($kreceive req rest kargs))
- (make-$kreceive (make-$arity req '() rest '() #f) kargs))
- ((_ ($kargs (name ...) (unquote syms) body))
- (make-$kargs (list name ...) syms (build-cps-term body)))
- ((_ ($kargs (name ...) (sym ...) body))
- (make-$kargs (list name ...) (list sym ...) (build-cps-term body)))
- ((_ ($kargs names syms body))
- (make-$kargs names syms (build-cps-term body)))
- ((_ ($kfun src meta self tail clause))
- (make-$kfun src meta self (build-cps-cont tail) (build-cps-cont clause)))
- ((_ ($ktail))
- (make-$ktail))
- ((_ ($kclause arity cont alternate))
- (make-$kclause (build-arity arity) (build-cps-cont cont)
- (build-cps-cont alternate)))))
-
-(define-syntax build-cps-cont
- (syntax-rules (unquote)
- ((_ (unquote exp)) exp)
- ((_ (k cont)) (make-$cont k (build-cont-body cont)))))
-
-(define-syntax build-cps-exp
- (syntax-rules (unquote
- $const $prim $fun $rec $closure $branch
- $call $callk $primcall $values $prompt)
- ((_ (unquote exp)) exp)
- ((_ ($const val)) (make-$const val))
- ((_ ($prim name)) (make-$prim name))
- ((_ ($fun body)) (make-$fun (build-cps-cont body)))
- ((_ ($rec names gensyms funs)) (make-$rec names gensyms funs))
- ((_ ($closure k nfree)) (make-$closure k nfree))
- ((_ ($call proc (unquote args))) (make-$call proc args))
- ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
- ((_ ($call proc args)) (make-$call proc args))
- ((_ ($callk k proc (unquote args))) (make-$callk k proc args))
- ((_ ($callk k proc (arg ...))) (make-$callk k proc (list arg ...)))
- ((_ ($callk k proc args)) (make-$callk k proc args))
- ((_ ($primcall name (unquote args))) (make-$primcall name args))
- ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
- ((_ ($primcall name args)) (make-$primcall name args))
- ((_ ($values (unquote args))) (make-$values args))
- ((_ ($values (arg ...))) (make-$values (list arg ...)))
- ((_ ($values args)) (make-$values args))
- ((_ ($branch k exp)) (make-$branch k (build-cps-exp exp)))
- ((_ ($prompt escape? tag handler))
- (make-$prompt escape? tag handler))))
-
-(define-syntax build-cps-term
- (syntax-rules (unquote $letk $letk* $letconst $program $continue)
- ((_ (unquote exp))
- exp)
- ((_ ($letk (unquote conts) body))
- (make-$letk conts (build-cps-term body)))
- ((_ ($letk (cont ...) body))
- (make-$letk (list (build-cps-cont cont) ...)
- (build-cps-term body)))
- ((_ ($letk* () body))
- (build-cps-term body))
- ((_ ($letk* (cont conts ...) body))
- (build-cps-term ($letk (cont) ($letk* (conts ...) body))))
- ((_ ($letconst () body))
- (build-cps-term body))
- ((_ ($letconst ((name sym val) tail ...) body))
- (let-fresh (kconst) ()
- (build-cps-term
- ($letk ((kconst ($kargs (name) (sym) ($letconst (tail ...) body))))
- ($continue kconst (let ((props (source-properties val)))
- (and (pair? props) props))
- ($const val))))))
- ((_ ($program (unquote conts)))
- (make-$program conts))
- ((_ ($program (cont ...)))
- (make-$program (list (build-cps-cont cont) ...)))
- ((_ ($program conts))
- (make-$program conts))
- ((_ ($continue k src exp))
- (make-$continue k src (build-cps-exp exp)))))
-
-(define-syntax-rule (rewrite-cps-term x (pat body) ...)
- (match x
- (pat (build-cps-term body)) ...))
-(define-syntax-rule (rewrite-cps-cont x (pat body) ...)
- (match x
- (pat (build-cps-cont body)) ...))
-(define-syntax-rule (rewrite-cps-exp x (pat body) ...)
- (match x
- (pat (build-cps-exp body)) ...))
-
-(define (parse-cps exp)
- (define (src exp)
- (let ((props (source-properties exp)))
- (and (pair? props) props)))
- (match exp
- ;; Continuations.
- (('letconst k (name sym c) body)
- (build-cps-term
- ($letk ((k ($kargs (name) (sym)
- ,(parse-cps body))))
- ($continue k (src exp) ($const c)))))
- (('let k (name sym val) body)
- (build-cps-term
- ($letk ((k ($kargs (name) (sym)
- ,(parse-cps body))))
- ,(parse-cps val))))
- (('letk (cont ...) body)
- (build-cps-term
- ($letk ,(map parse-cps cont) ,(parse-cps body))))
- (('k sym body)
- (build-cps-cont
- (sym ,(parse-cps body))))
- (('kreceive req rest k)
- (build-cont-body ($kreceive req rest k)))
- (('kargs names syms body)
- (build-cont-body ($kargs names syms ,(parse-cps body))))
- (('kfun src meta self tail clause)
- (build-cont-body
- ($kfun (src exp) meta self ,(parse-cps tail)
- ,(and=> clause parse-cps))))
- (('ktail)
- (build-cont-body
- ($ktail)))
- (('kclause (req opt rest kw allow-other-keys?) body)
- (build-cont-body
- ($kclause (req opt rest kw allow-other-keys?)
- ,(parse-cps body)
- ,#f)))
- (('kclause (req opt rest kw allow-other-keys?) body alternate)
- (build-cont-body
- ($kclause (req opt rest kw allow-other-keys?)
- ,(parse-cps body)
- ,(parse-cps alternate))))
- (('kseq body)
- (build-cont-body ($kargs () () ,(parse-cps body))))
-
- ;; Calls.
- (('continue k exp)
- (build-cps-term ($continue k (src exp) ,(parse-cps exp))))
- (('const exp)
- (build-cps-exp ($const exp)))
- (('prim name)
- (build-cps-exp ($prim name)))
- (('fun body)
- (build-cps-exp ($fun ,(parse-cps body))))
- (('closure k nfree)
- (build-cps-exp ($closure k nfree)))
- (('rec (name sym fun) ...)
- (build-cps-exp ($rec name sym (map parse-cps fun))))
- (('program (cont ...))
- (build-cps-term ($program ,(map parse-cps cont))))
- (('call proc arg ...)
- (build-cps-exp ($call proc arg)))
- (('callk k proc arg ...)
- (build-cps-exp ($callk k proc arg)))
- (('primcall name arg ...)
- (build-cps-exp ($primcall name arg)))
- (('branch k exp)
- (build-cps-exp ($branch k ,(parse-cps exp))))
- (('values arg ...)
- (build-cps-exp ($values arg)))
- (('prompt escape? tag handler)
- (build-cps-exp ($prompt escape? tag handler)))
- (_
- (error "unexpected cps" exp))))
-
-(define (unparse-cps exp)
- (match exp
- ;; Continuations.
- (($ $letk (($ $cont k ($ $kargs (name) (sym) body)))
- ($ $continue k src ($ $const c)))
- `(letconst ,k (,name ,sym ,c)
- ,(unparse-cps body)))
- (($ $letk (($ $cont k ($ $kargs (name) (sym) body))) val)
- `(let ,k (,name ,sym ,(unparse-cps val))
- ,(unparse-cps body)))
- (($ $letk conts body)
- `(letk ,(map unparse-cps conts) ,(unparse-cps body)))
- (($ $cont sym body)
- `(k ,sym ,(unparse-cps body)))
- (($ $kreceive ($ $arity req () rest '() #f) k)
- `(kreceive ,req ,rest ,k))
- (($ $kargs () () body)
- `(kseq ,(unparse-cps body)))
- (($ $kargs names syms body)
- `(kargs ,names ,syms ,(unparse-cps body)))
- (($ $kfun src meta self tail clause)
- `(kfun ,meta ,self ,(unparse-cps tail) ,(unparse-cps clause)))
- (($ $ktail)
- `(ktail))
- (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alternate)
- `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,(unparse-cps body)
- . ,(if alternate (list (unparse-cps alternate)) '())))
-
- ;; Calls.
- (($ $continue k src exp)
- `(continue ,k ,(unparse-cps exp)))
- (($ $const val)
- `(const ,val))
- (($ $prim name)
- `(prim ,name))
- (($ $fun body)
- `(fun ,(unparse-cps body)))
- (($ $closure k nfree)
- `(closure ,k ,nfree))
- (($ $rec names syms funs)
- `(rec ,@(map (lambda (name sym fun)
- (list name sym (unparse-cps fun)))
- names syms funs)))
- (($ $program conts)
- `(program ,(map unparse-cps conts)))
- (($ $call proc args)
- `(call ,proc ,@args))
- (($ $callk k proc args)
- `(callk ,k ,proc ,@args))
- (($ $primcall name args)
- `(primcall ,name ,@args))
- (($ $branch k exp)
- `(branch ,k ,(unparse-cps exp)))
- (($ $values args)
- `(values ,@args))
- (($ $prompt escape? tag handler)
- `(prompt ,escape? ,tag ,handler))
- (_
- (error "unexpected cps" exp))))
-
-(define-syntax-rule (make-global-cont-folder seed ...)
- (lambda (proc cont seed ...)
- (define (cont-folder cont seed ...)
- (match cont
- (($ $cont k cont)
- (let-values (((seed ...) (proc k cont seed ...)))
- (match cont
- (($ $kargs names syms body)
- (term-folder body seed ...))
-
- (($ $kfun src meta self tail clause)
- (let-values (((seed ...) (cont-folder tail seed ...)))
- (if clause
- (cont-folder clause seed ...)
- (values seed ...))))
-
- (($ $kclause arity body alternate)
- (let-values (((seed ...) (cont-folder body seed ...)))
- (if alternate
- (cont-folder alternate seed ...)
- (values seed ...))))
-
- (_ (values seed ...)))))))
-
- (define (fun-folder fun seed ...)
- (match fun
- (($ $fun body)
- (cont-folder body seed ...))))
-
- (define (term-folder term seed ...)
- (match term
- (($ $letk conts body)
- (let-values (((seed ...) (term-folder body seed ...)))
- (let lp ((conts conts) (seed seed) ...)
- (if (null? conts)
- (values seed ...)
- (let-values (((seed ...) (cont-folder (car conts) seed ...)))
- (lp (cdr conts) seed ...))))))
-
- (($ $continue k src exp)
- (match exp
- (($ $fun) (fun-folder exp seed ...))
- (($ $rec names syms funs)
- (let lp ((funs funs) (seed seed) ...)
- (if (null? funs)
- (values seed ...)
- (let-values (((seed ...) (fun-folder (car funs) seed ...)))
- (lp (cdr funs) seed ...)))))
- (_ (values seed ...))))))
-
- (cont-folder cont seed ...)))
-
-(define-syntax-rule (make-local-cont-folder seed ...)
- (lambda (proc cont seed ...)
- (define (cont-folder cont seed ...)
- (match cont
- (($ $cont k (and cont ($ $kargs names syms body)))
- (let-values (((seed ...) (proc k cont seed ...)))
- (term-folder body seed ...)))
- (($ $cont k cont)
- (proc k cont seed ...))))
- (define (term-folder term seed ...)
- (match term
- (($ $letk conts body)
- (let-values (((seed ...) (term-folder body seed ...)))
- (let lp ((conts conts) (seed seed) ...)
- (match conts
- (() (values seed ...))
- ((cont) (cont-folder cont seed ...))
- ((cont . conts)
- (let-values (((seed ...) (cont-folder cont seed ...)))
- (lp conts seed ...)))))))
- (_ (values seed ...))))
- (define (clause-folder clause seed ...)
- (match clause
- (($ $cont k (and cont ($ $kclause arity body alternate)))
- (let-values (((seed ...) (proc k cont seed ...)))
- (if alternate
- (let-values (((seed ...) (cont-folder body seed ...)))
- (clause-folder alternate seed ...))
- (cont-folder body seed ...))))))
- (match cont
- (($ $cont k (and cont ($ $kfun src meta self tail clause)))
- (let*-values (((seed ...) (proc k cont seed ...))
- ((seed ...) (if clause
- (clause-folder clause seed ...)
- (values seed ...))))
- (cont-folder tail seed ...))))))
-
-(define (compute-max-label-and-var fun)
- (match fun
- (($ $cont)
- ((make-global-cont-folder max-label max-var)
- (lambda (label cont max-label max-var)
- (values (max label max-label)
- (match cont
- (($ $kargs names vars body)
- (fold max max-var vars))
- (($ $kfun src meta self)
- (max self max-var))
- (_ max-var))))
- fun -1 -1))
- (($ $program conts)
- (define (fold/2 proc in s0 s1)
- (if (null? in)
- (values s0 s1)
- (let-values (((s0 s1) (proc (car in) s0 s1)))
- (fold/2 proc (cdr in) s0 s1))))
- (let lp ((conts conts) (max-label -1) (max-var -1))
- (if (null? conts)
- (values max-label max-var)
- (call-with-values (lambda ()
- ((make-local-cont-folder max-label max-var)
- (lambda (label cont max-label max-var)
- (values (max label max-label)
- (match cont
- (($ $kargs names vars body)
- (fold max max-var vars))
- (($ $kfun src meta self)
- (max self max-var))
- (_ max-var))))
- (car conts) max-label max-var))
- (lambda (max-label max-var)
- (lp (cdr conts) max-label max-var))))))))
-
-(define (fold-conts proc seed fun)
- ((make-global-cont-folder seed) proc fun seed))
-
-(define (fold-local-conts proc seed fun)
- ((make-local-cont-folder seed) proc fun seed))
-
-(define (visit-cont-successors proc cont)
- (match cont
- (($ $kargs names syms body)
- (let lp ((body body))
- (match body
- (($ $letk conts body) (lp body))
- (($ $continue k src exp)
- (match exp
- (($ $prompt escape? tag handler) (proc k handler))
- (($ $branch kt) (proc k kt))
- (_ (proc k)))))))
-
- (($ $kreceive arity k) (proc k))
-
- (($ $kclause arity ($ $cont kbody) #f) (proc kbody))
-
- (($ $kclause arity ($ $cont kbody) ($ $cont kalt)) (proc kbody kalt))
-
- (($ $kfun src meta self tail ($ $cont clause)) (proc clause))
-
- (($ $kfun src meta self tail #f) (proc))
-
- (($ $ktail) (proc))))
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
deleted file mode 100644
index c92c15d01..000000000
--- a/module/language/cps/compile-bytecode.scm
+++ /dev/null
@@ -1,453 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Compiling CPS to bytecode. The result is in the bytecode language,
-;;; which happens to be an ELF image as a bytecode.
-;;;
-;;; Code:
-
-(define-module (language cps compile-bytecode)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps primitives)
- #:use-module (language cps renumber)
- #:use-module (language cps slot-allocation)
- #:use-module (system vm assembler)
- #:export (compile-bytecode))
-
-(define (kw-arg-ref args kw default)
- (match (memq kw args)
- ((_ val . _) val)
- (_ default)))
-
-(define (compile-fun f asm)
- (let* ((dfg (compute-dfg f #:global? #f))
- (allocation (allocate-slots f dfg)))
- (define (maybe-slot sym)
- (lookup-maybe-slot sym allocation))
-
- (define (slot sym)
- (lookup-slot sym allocation))
-
- (define (constant sym)
- (lookup-constant-value sym allocation))
-
- (define (maybe-mov dst src)
- (unless (= dst src)
- (emit-mov asm dst src)))
-
- (define (maybe-load-constant slot src)
- (call-with-values (lambda ()
- (lookup-maybe-constant-value src allocation))
- (lambda (has-const? val)
- (and has-const?
- (begin
- (emit-load-constant asm slot val)
- #t)))))
-
- (define (compile-entry)
- (let ((label (dfg-min-label dfg)))
- (match (lookup-cont label dfg)
- (($ $kfun src meta self tail clause)
- (when src
- (emit-source asm src))
- (emit-begin-program asm label meta)
- (compile-clause (1+ label))
- (emit-end-program asm)))))
-
- (define (compile-clause label)
- (match (lookup-cont label dfg)
- (($ $kclause ($ $arity req opt rest kw allow-other-keys?)
- body alternate)
- (let* ((kw-indices (map (match-lambda
- ((key name sym)
- (cons key (lookup-slot sym allocation))))
- kw))
- (nlocals (lookup-nlocals label allocation)))
- (emit-label asm label)
- (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
- nlocals
- (match alternate (#f #f) (($ $cont alt) alt)))
- (let ((next (compile-body (1+ label) nlocals)))
- (emit-end-arity asm)
- (match alternate
- (($ $cont alt)
- (unless (eq? next alt)
- (error "unexpected k" alt))
- (compile-clause next))
- (#f
- (unless (= next (+ (dfg-min-label dfg) (dfg-label-count dfg)))
- (error "unexpected end of clauses")))))))))
-
- (define (compile-body label nlocals)
- (let compile-cont ((label label))
- (if (eq? label (+ (dfg-min-label dfg) (dfg-label-count dfg)))
- label
- (match (lookup-cont label dfg)
- (($ $kclause) label)
- (($ $kargs names vars term)
- (emit-label asm label)
- (for-each (lambda (name var)
- (let ((slot (maybe-slot var)))
- (when slot
- (emit-definition asm name slot))))
- names vars)
- (let find-exp ((term term))
- (match term
- (($ $letk conts term)
- (find-exp term))
- (($ $continue k src exp)
- (when src
- (emit-source asm src))
- (compile-expression label k exp nlocals)
- (compile-cont (1+ label))))))
- (_
- (emit-label asm label)
- (compile-cont (1+ label)))))))
-
- (define (compile-expression label k exp nlocals)
- (let* ((fallthrough? (= k (1+ label))))
- (define (maybe-emit-jump)
- (unless fallthrough?
- (emit-br asm k)))
- (match (lookup-cont k dfg)
- (($ $ktail)
- (compile-tail label exp))
- (($ $kargs (name) (sym))
- (let ((dst (maybe-slot sym)))
- (when dst
- (compile-value label exp dst nlocals)))
- (maybe-emit-jump))
- (($ $kargs () ())
- (match exp
- (($ $branch kt exp)
- (compile-test label exp kt k (1+ label)))
- (_
- (compile-effect label exp k nlocals)
- (maybe-emit-jump))))
- (($ $kargs names syms)
- (compile-values label exp syms)
- (maybe-emit-jump))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (compile-trunc label k exp (length req)
- (and rest
- (match (lookup-cont kargs dfg)
- (($ $kargs names (_ ... rest)) rest)))
- nlocals)
- (unless (and fallthrough? (= kargs (1+ k)))
- (emit-br asm kargs))))))
-
- (define (compile-tail label exp)
- ;; There are only three kinds of expressions in tail position:
- ;; tail calls, multiple-value returns, and single-value returns.
- (match exp
- (($ $call proc args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (let ((tail-slots (cdr (iota (1+ (length args))))))
- (for-each maybe-load-constant tail-slots args))
- (emit-tail-call asm (1+ (length args))))
- (($ $callk k proc args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (let ((tail-slots (cdr (iota (1+ (length args))))))
- (for-each maybe-load-constant tail-slots args))
- (emit-tail-call-label asm (1+ (length args)) k))
- (($ $values ())
- (emit-reset-frame asm 1)
- (emit-return-values asm))
- (($ $values (arg))
- (if (maybe-slot arg)
- (emit-return asm (slot arg))
- (begin
- (emit-load-constant asm 1 (constant arg))
- (emit-return asm 1))))
- (($ $values args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (let ((tail-slots (cdr (iota (1+ (length args))))))
- (for-each maybe-load-constant tail-slots args))
- (emit-reset-frame asm (1+ (length args)))
- (emit-return-values asm))
- (($ $primcall 'return (arg))
- (emit-return asm (slot arg)))))
-
- (define (compile-value label exp dst nlocals)
- (match exp
- (($ $values (arg))
- (or (maybe-load-constant dst arg)
- (maybe-mov dst (slot arg))))
- (($ $const exp)
- (emit-load-constant asm dst exp))
- (($ $closure k 0)
- (emit-load-static-procedure asm dst k))
- (($ $closure k nfree)
- (emit-make-closure asm dst k nfree))
- (($ $primcall 'current-module)
- (emit-current-module asm dst))
- (($ $primcall 'cached-toplevel-box (scope name bound?))
- (emit-cached-toplevel-box asm dst (constant scope) (constant name)
- (constant bound?)))
- (($ $primcall 'cached-module-box (mod name public? bound?))
- (emit-cached-module-box asm dst (constant mod) (constant name)
- (constant public?) (constant bound?)))
- (($ $primcall 'resolve (name bound?))
- (emit-resolve asm dst (constant bound?) (slot name)))
- (($ $primcall 'free-ref (closure idx))
- (emit-free-ref asm dst (slot closure) (constant idx)))
- (($ $primcall 'vector-ref (vector index))
- (emit-vector-ref asm dst (slot vector) (slot index)))
- (($ $primcall 'make-vector (length init))
- (emit-make-vector asm dst (slot length) (slot init)))
- (($ $primcall 'make-vector/immediate (length init))
- (emit-make-vector/immediate asm dst (constant length) (slot init)))
- (($ $primcall 'vector-ref/immediate (vector index))
- (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
- (($ $primcall 'allocate-struct (vtable nfields))
- (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
- (($ $primcall 'allocate-struct/immediate (vtable nfields))
- (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
- (($ $primcall 'struct-ref (struct n))
- (emit-struct-ref asm dst (slot struct) (slot n)))
- (($ $primcall 'struct-ref/immediate (struct n))
- (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
- (($ $primcall 'builtin-ref (name))
- (emit-builtin-ref asm dst (constant name)))
- (($ $primcall 'bv-u8-ref (bv idx))
- (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s8-ref (bv idx))
- (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u16-ref (bv idx))
- (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s16-ref (bv idx))
- (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u32-ref (bv idx val))
- (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s32-ref (bv idx val))
- (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u64-ref (bv idx val))
- (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s64-ref (bv idx val))
- (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-f32-ref (bv idx val))
- (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-f64-ref (bv idx val))
- (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall name args)
- ;; FIXME: Inline all the cases.
- (let ((inst (prim-instruction name)))
- (emit-text asm `((,inst ,dst ,@(map slot args))))))))
-
- (define (compile-effect label exp k nlocals)
- (match exp
- (($ $values ()) #f)
- (($ $prompt escape? tag handler)
- (match (lookup-cont handler dfg)
- (($ $kreceive ($ $arity req () rest () #f) khandler-body)
- (let ((receive-args (gensym "handler"))
- (nreq (length req))
- (proc-slot (lookup-call-proc-slot handler allocation)))
- (emit-prompt asm (slot tag) escape? proc-slot receive-args)
- (emit-br asm k)
- (emit-label asm receive-args)
- (unless (and rest (zero? nreq))
- (emit-receive-values asm proc-slot (->bool rest) nreq))
- (when (and rest
- (match (lookup-cont khandler-body dfg)
- (($ $kargs names (_ ... rest))
- (maybe-slot rest))))
- (emit-bind-rest asm (+ proc-slot 1 nreq)))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves handler allocation))
- (emit-reset-frame asm nlocals)
- (emit-br asm khandler-body)))))
- (($ $primcall 'cache-current-module! (sym scope))
- (emit-cache-current-module! asm (slot sym) (constant scope)))
- (($ $primcall 'free-set! (closure idx value))
- (emit-free-set! asm (slot closure) (slot value) (constant idx)))
- (($ $primcall 'box-set! (box value))
- (emit-box-set! asm (slot box) (slot value)))
- (($ $primcall 'struct-set! (struct index value))
- (emit-struct-set! asm (slot struct) (slot index) (slot value)))
- (($ $primcall 'struct-set!/immediate (struct index value))
- (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
- (($ $primcall 'vector-set! (vector index value))
- (emit-vector-set! asm (slot vector) (slot index) (slot value)))
- (($ $primcall 'vector-set!/immediate (vector index value))
- (emit-vector-set!/immediate asm (slot vector) (constant index)
- (slot value)))
- (($ $primcall 'set-car! (pair value))
- (emit-set-car! asm (slot pair) (slot value)))
- (($ $primcall 'set-cdr! (pair value))
- (emit-set-cdr! asm (slot pair) (slot value)))
- (($ $primcall 'define! (sym value))
- (emit-define! asm (slot sym) (slot value)))
- (($ $primcall 'push-fluid (fluid val))
- (emit-push-fluid asm (slot fluid) (slot val)))
- (($ $primcall 'pop-fluid ())
- (emit-pop-fluid asm))
- (($ $primcall 'wind (winder unwinder))
- (emit-wind asm (slot winder) (slot unwinder)))
- (($ $primcall 'bv-u8-set! (bv idx val))
- (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s8-set! (bv idx val))
- (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u16-set! (bv idx val))
- (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s16-set! (bv idx val))
- (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u32-set! (bv idx val))
- (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s32-set! (bv idx val))
- (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u64-set! (bv idx val))
- (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s64-set! (bv idx val))
- (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-f32-set! (bv idx val))
- (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-f64-set! (bv idx val))
- (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'unwind ())
- (emit-unwind asm))))
-
- (define (compile-values label exp syms)
- (match exp
- (($ $values args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (for-each maybe-load-constant (map slot syms) args))))
-
- (define (compile-test label exp kt kf next-label)
- (define (unary op sym)
- (cond
- ((eq? kt next-label)
- (op asm (slot sym) #t kf))
- (else
- (op asm (slot sym) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (define (binary op a b)
- (cond
- ((eq? kt next-label)
- (op asm (slot a) (slot b) #t kf))
- (else
- (op asm (slot a) (slot b) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (match exp
- (($ $values (sym))
- (call-with-values (lambda ()
- (lookup-maybe-constant-value sym allocation))
- (lambda (has-const? val)
- (if has-const?
- (if val
- (unless (eq? kt next-label)
- (emit-br asm kt))
- (unless (eq? kf next-label)
- (emit-br asm kf)))
- (unary emit-br-if-true sym)))))
- (($ $primcall 'null? (a)) (unary emit-br-if-null a))
- (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
- (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
- (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
- (($ $primcall 'char? (a)) (unary emit-br-if-char a))
- (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
- (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
- (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
- (($ $primcall 'string? (a)) (unary emit-br-if-string a))
- (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
- (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
- (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
- ;; Add more TC7 tests here. Keep in sync with
- ;; *branching-primcall-arities* in (language cps primitives) and
- ;; the set of macro-instructions in assembly.scm.
- (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
- (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
- (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
- (($ $primcall '< (a b)) (binary emit-br-if-< a b))
- (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
- (($ $primcall '= (a b)) (binary emit-br-if-= a b))
- (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
- (($ $primcall '> (a b)) (binary emit-br-if-< b a))
- (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
-
- (define (compile-trunc label k exp nreq rest-var nlocals)
- (define (do-call proc args emit-call)
- (let* ((proc-slot (lookup-call-proc-slot label allocation))
- (nargs (1+ (length args)))
- (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (for-each maybe-load-constant arg-slots (cons proc args))
- (emit-call asm proc-slot nargs)
- (emit-dead-slot-map asm proc-slot
- (lookup-dead-slot-map label allocation))
- (cond
- ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
- (match (lookup-parallel-moves k allocation)
- ((((? (lambda (src) (= src (1+ proc-slot))) src)
- . dst)) dst)
- (_ #f)))
- ;; The usual case: one required live return value, ignoring
- ;; any additional values.
- => (lambda (dst)
- (emit-receive asm dst proc-slot nlocals)))
- (else
- (unless (and (zero? nreq) rest-var)
- (emit-receive-values asm proc-slot (->bool rest-var) nreq))
- (when (and rest-var (maybe-slot rest-var))
- (emit-bind-rest asm (+ proc-slot 1 nreq)))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves k allocation))
- (emit-reset-frame asm nlocals)))))
- (match exp
- (($ $call proc args)
- (do-call proc args
- (lambda (asm proc-slot nargs)
- (emit-call asm proc-slot nargs))))
- (($ $callk k proc args)
- (do-call proc args
- (lambda (asm proc-slot nargs)
- (emit-call-label asm proc-slot nargs k))))))
-
- (match f
- (($ $cont k ($ $kfun src meta self tail clause))
- (compile-entry)))))
-
-(define (compile-bytecode exp env opts)
- (let* ((exp (renumber exp))
- (asm (make-assembler)))
- (match exp
- (($ $program funs)
- (for-each (lambda (fun) (compile-fun fun asm))
- funs)))
- (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
- env
- env)))
diff --git a/module/language/cps/dfg.scm b/module/language/cps/dfg.scm
deleted file mode 100644
index 22bc15900..000000000
--- a/module/language/cps/dfg.scm
+++ /dev/null
@@ -1,904 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Many passes rely on a local or global static analysis of a function.
-;;; This module implements a simple data-flow graph (DFG) analysis,
-;;; tracking the definitions and uses of variables and continuations.
-;;; It also builds a table of continuations and scope links, to be able
-;;; to easily determine if one continuation is in the scope of another,
-;;; and to get to the expression inside a continuation.
-;;;
-;;; Note that the data-flow graph of continuation labels is a
-;;; control-flow graph.
-;;;
-;;; We currently don't expose details of the DFG type outside this
-;;; module, preferring to only expose accessors. That may change in the
-;;; future but it seems to work for now.
-;;;
-;;; Code:
-
-(define-module (language cps dfg)
- #:use-module (ice-9 match)
- #:use-module (ice-9 format)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps intset)
- #:export (build-cont-table
- lookup-cont
-
- compute-dfg
- dfg-cont-table
- dfg-min-label
- dfg-label-count
- dfg-min-var
- dfg-var-count
- with-fresh-name-state-from-dfg
- lookup-def
- lookup-uses
- lookup-predecessors
- lookup-successors
- lookup-block-scope
- find-call
- call-expression
- find-expression
- find-defining-expression
- find-constant-value
- continuation-bound-in?
- variable-free-in?
- constant-needs-allocation?
- control-point?
- lookup-bound-syms
-
- compute-idoms
- compute-dom-edges
-
- ;; Data flow analysis.
- compute-live-variables
- dfa-k-idx dfa-k-sym dfa-k-count dfa-k-in dfa-k-out
- dfa-var-idx dfa-var-sym dfa-var-count
- print-dfa))
-
-;; These definitions are here because currently we don't do cross-module
-;; inlining. They can be removed once that restriction is gone.
-(define-inlinable (for-each f l)
- (unless (list? l)
- (scm-error 'wrong-type-arg "for-each" "Not a list: ~S" (list l) #f))
- (let for-each1 ((l l))
- (unless (null? l)
- (f (car l))
- (for-each1 (cdr l)))))
-
-(define-inlinable (for-each/2 f l1 l2)
- (unless (= (length l1) (length l2))
- (scm-error 'wrong-type-arg "for-each" "List of wrong length: ~S"
- (list l2) #f))
- (let for-each2 ((l1 l1) (l2 l2))
- (unless (null? l1)
- (f (car l1) (car l2))
- (for-each2 (cdr l1) (cdr l2)))))
-
-(define (build-cont-table fun)
- (let ((max-k (fold-conts (lambda (k cont max-k) (max k max-k))
- -1 fun)))
- (fold-conts (lambda (k cont table)
- (vector-set! table k cont)
- table)
- (make-vector (1+ max-k) #f)
- fun)))
-
-;; Data-flow graph for CPS: both for values and continuations.
-(define-record-type $dfg
- (make-dfg conts preds defs uses scopes scope-levels
- min-label max-label label-count
- min-var max-var var-count)
- dfg?
- ;; vector of label -> $kargs, etc
- (conts dfg-cont-table)
- ;; vector of label -> (pred-label ...)
- (preds dfg-preds)
- ;; vector of var -> def-label
- (defs dfg-defs)
- ;; vector of var -> (use-label ...)
- (uses dfg-uses)
- ;; vector of label -> label
- (scopes dfg-scopes)
- ;; vector of label -> int
- (scope-levels dfg-scope-levels)
-
- (min-label dfg-min-label)
- (max-label dfg-max-label)
- (label-count dfg-label-count)
-
- (min-var dfg-min-var)
- (max-var dfg-max-var)
- (var-count dfg-var-count))
-
-(define-inlinable (vector-push! vec idx val)
- (let ((v vec) (i idx))
- (vector-set! v i (cons val (vector-ref v i)))))
-
-(define (compute-reachable dfg min-label label-count)
- "Compute and return the continuations that may be reached if flow
-reaches a continuation N. Returns a vector of intsets, whose first
-index corresponds to MIN-LABEL, and so on."
- (let (;; Vector of intsets, indicating that continuation N can
- ;; reach a set M...
- (reachable (make-vector label-count #f)))
-
- (define (label->idx label) (- label min-label))
-
- ;; Iterate labels backwards, to converge quickly.
- (let lp ((label (+ min-label label-count)) (changed? #f))
- (cond
- ((= label min-label)
- (if changed?
- (lp (+ min-label label-count) #f)
- reachable))
- (else
- (let* ((label (1- label))
- (idx (label->idx label))
- (old (vector-ref reachable idx))
- (new (fold (lambda (succ set)
- (cond
- ((vector-ref reachable (label->idx succ))
- => (lambda (succ-set)
- (intset-union set succ-set)))
- (else set)))
- (or (vector-ref reachable idx)
- (intset-add empty-intset label))
- (visit-cont-successors list
- (lookup-cont label dfg)))))
- (cond
- ((eq? old new)
- (lp label changed?))
- (else
- (vector-set! reachable idx new)
- (lp label #t)))))))))
-
-(define (find-prompts dfg min-label label-count)
- "Find the prompts in DFG between MIN-LABEL and MIN-LABEL +
-LABEL-COUNT, and return them as a list of PROMPT-LABEL, HANDLER-LABEL
-pairs."
- (let lp ((label min-label) (prompts '()))
- (cond
- ((= label (+ min-label label-count))
- (reverse prompts))
- (else
- (match (lookup-cont label dfg)
- (($ $kargs names syms body)
- (match (find-expression body)
- (($ $prompt escape? tag handler)
- (lp (1+ label) (acons label handler prompts)))
- (_ (lp (1+ label) prompts))))
- (_ (lp (1+ label) prompts)))))))
-
-(define (compute-interval reachable min-label label-count start end)
- "Compute and return the set of continuations that may be reached from
-START, inclusive, but not reached by END, exclusive. Returns an
-intset."
- (intset-subtract (vector-ref reachable (- start min-label))
- (vector-ref reachable (- end min-label))))
-
-(define (find-prompt-bodies dfg min-label label-count)
- "Find all the prompts in DFG from the LABEL-COUNT continuations
-starting at MIN-LABEL, and compute the set of continuations that is
-reachable from the prompt bodies but not from the corresponding handler.
-Returns a list of PROMPT, HANDLER, BODY lists, where the BODY is an
-intset."
- (match (find-prompts dfg min-label label-count)
- (() '())
- (((prompt . handler) ...)
- (let ((reachable (compute-reachable dfg min-label label-count)))
- (map (lambda (prompt handler)
- ;; FIXME: It isn't correct to use all continuations
- ;; reachable from the prompt, because that includes
- ;; continuations outside the prompt body. This point is
- ;; moot if the handler's control flow joins with the the
- ;; body, as is usually but not always the case.
- ;;
- ;; One counter-example is when the handler contifies an
- ;; infinite loop; in that case we compute a too-large
- ;; prompt body. This error is currently innocuous, but we
- ;; should fix it at some point.
- ;;
- ;; The fix is to end the body at the corresponding "pop"
- ;; primcall, if any.
- (let ((body (compute-interval reachable min-label label-count
- prompt handler)))
- (list prompt handler body)))
- prompt handler)))))
-
-(define* (visit-prompt-control-flow dfg min-label label-count f #:key complete?)
- "For all prompts in DFG in the range [MIN-LABEL, MIN-LABEL +
-LABEL-COUNT), invoke F with arguments PROMPT, HANDLER, and BODY for each
-body continuation in the prompt."
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (for-each
- (match-lambda
- ((prompt handler body)
- (define (out-or-back-edge? label)
- ;; Most uses of visit-prompt-control-flow don't need every body
- ;; continuation, and would be happy getting called only for
- ;; continuations that postdominate the rest of the body. Unless
- ;; you pass #:complete? #t, we only invoke F on continuations
- ;; that can leave the body, or on back-edges in loops.
- ;;
- ;; You would think that looking for the final "pop" primcall
- ;; would be sufficient, but that is incorrect; it's possible for
- ;; a loop in the prompt body to be contified, and that loop need
- ;; not continue to the pop if it never terminates. The pop could
- ;; even be removed by DCE, in that case.
- (or-map (lambda (succ)
- (or (not (intset-ref body succ))
- (<= succ label)))
- (lookup-successors label dfg)))
- (let lp ((label min-label))
- (let ((label (intset-next body label)))
- (when label
- (when (or complete? (out-or-back-edge? label))
- (f prompt handler label))
- (lp (1+ label)))))))
- (find-prompt-bodies dfg min-label label-count)))
-
-(define (analyze-reverse-control-flow fun dfg min-label label-count)
- (define (compute-reverse-control-flow-order ktail dfg)
- (let ((label-map (make-vector label-count #f))
- (next -1))
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
-
- (let visit ((k ktail))
- ;; Mark this label as visited.
- (vector-set! label-map (label->idx k) #t)
- (for-each (lambda (k)
- ;; Visit predecessors unless they are already visited.
- (unless (vector-ref label-map (label->idx k))
- (visit k)))
- (lookup-predecessors k dfg))
- ;; Add to reverse post-order chain.
- (vector-set! label-map (label->idx k) next)
- (set! next k))
-
- (let lp ((n 0) (head next))
- (if (< head 0)
- ;; Add nodes that are not reachable from the tail.
- (let lp ((n n) (m label-count))
- (unless (= n label-count)
- (let find-unvisited ((m (1- m)))
- (if (vector-ref label-map m)
- (find-unvisited (1- m))
- (begin
- (vector-set! label-map m n)
- (lp (1+ n) m))))))
- ;; Pop the head off the chain, give it its
- ;; reverse-post-order numbering, and continue.
- (let ((next (vector-ref label-map (label->idx head))))
- (vector-set! label-map (label->idx head) n)
- (lp (1+ n) next))))
-
- label-map))
-
- (define (convert-successors k-map)
- (define (idx->label idx) (+ idx min-label))
- (define (renumber label)
- (vector-ref k-map (- label min-label)))
- (let ((succs (make-vector (vector-length k-map) #f)))
- (let lp ((n 0))
- (when (< n (vector-length succs))
- (vector-set! succs (vector-ref k-map n)
- (map renumber
- (lookup-successors (idx->label n) dfg)))
- (lp (1+ n))))
- succs))
-
- (match fun
- (($ $cont kfun ($ $kfun src meta self ($ $cont ktail tail)))
- (let* ((k-map (compute-reverse-control-flow-order ktail dfg))
- (succs (convert-successors k-map)))
- ;; Any expression in the prompt body could cause an abort to
- ;; the handler. This code adds links from every block in the
- ;; prompt body to the handler. This causes all values used
- ;; by the handler to be seen as live in the prompt body, as
- ;; indeed they are.
- (visit-prompt-control-flow
- dfg min-label label-count
- (lambda (prompt handler body)
- (define (renumber label)
- (vector-ref k-map (- label min-label)))
- (vector-push! succs (renumber body) (renumber handler))))
-
- (values k-map succs)))))
-
-(define (compute-idoms dfg min-label label-count)
- (define preds (dfg-preds dfg))
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (define (idx->dfg-idx idx) (- (idx->label idx) (dfg-min-label dfg)))
- (let ((idoms (make-vector label-count #f)))
- (define (common-idom d0 d1)
- ;; We exploit the fact that a reverse post-order is a topological
- ;; sort, and so the idom of a node is always numerically less than
- ;; the node itself.
- (cond
- ((= d0 d1) d0)
- ((< d0 d1) (common-idom d0 (vector-ref idoms (label->idx d1))))
- (else (common-idom (vector-ref idoms (label->idx d0)) d1))))
- (define (compute-idom preds)
- (define (has-idom? pred)
- (vector-ref idoms (label->idx pred)))
- (match preds
- (() min-label)
- ((pred . preds)
- (if (has-idom? pred)
- (let lp ((idom pred) (preds preds))
- (match preds
- (() idom)
- ((pred . preds)
- (lp (if (has-idom? pred)
- (common-idom idom pred)
- idom)
- preds))))
- (compute-idom preds)))))
- ;; This is the iterative O(n^2) fixpoint algorithm, originally from
- ;; Allen and Cocke ("Graph-theoretic constructs for program flow
- ;; analysis", 1972). See the discussion in Cooper, Harvey, and
- ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
- (let iterate ((n 0) (changed? #f))
- (cond
- ((< n label-count)
- (let ((idom (vector-ref idoms n))
- (idom* (compute-idom (vector-ref preds (idx->dfg-idx n)))))
- (cond
- ((eqv? idom idom*)
- (iterate (1+ n) changed?))
- (else
- (vector-set! idoms n idom*)
- (iterate (1+ n) #t)))))
- (changed?
- (iterate 0 #f))
- (else idoms)))))
-
-;; Compute a vector containing, for each node, a list of the nodes that
-;; it immediately dominates. These are the "D" edges in the DJ tree.
-(define (compute-dom-edges idoms min-label)
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (let ((doms (make-vector (vector-length idoms) '())))
- (let lp ((n 0))
- (when (< n (vector-length idoms))
- (let ((idom (vector-ref idoms n)))
- (vector-push! doms (label->idx idom) (idx->label n)))
- (lp (1+ n))))
- doms))
-
-;; There used to be some loop detection code here, but it bitrotted.
-;; We'll need it again eventually but for now it can be found in the git
-;; history.
-
-;; Data-flow analysis.
-(define-record-type $dfa
- (make-dfa min-label min-var var-count in out)
- dfa?
- ;; Minimum label in this function.
- (min-label dfa-min-label)
- ;; Minimum var in this function.
- (min-var dfa-min-var)
- ;; Var count in this function.
- (var-count dfa-var-count)
- ;; Vector of k-idx -> intset
- (in dfa-in)
- ;; Vector of k-idx -> intset
- (out dfa-out))
-
-(define (dfa-k-idx dfa k)
- (- k (dfa-min-label dfa)))
-
-(define (dfa-k-sym dfa idx)
- (+ idx (dfa-min-label dfa)))
-
-(define (dfa-k-count dfa)
- (vector-length (dfa-in dfa)))
-
-(define (dfa-var-idx dfa var)
- (let ((idx (- var (dfa-min-var dfa))))
- (unless (< -1 idx (dfa-var-count dfa))
- (error "var out of range" var))
- idx))
-
-(define (dfa-var-sym dfa idx)
- (unless (< -1 idx (dfa-var-count dfa))
- (error "idx out of range" idx))
- (+ idx (dfa-min-var dfa)))
-
-(define (dfa-k-in dfa idx)
- (vector-ref (dfa-in dfa) idx))
-
-(define (dfa-k-out dfa idx)
- (vector-ref (dfa-out dfa) idx))
-
-(define (compute-live-variables fun dfg)
- ;; Compute the maximum fixed point of the data-flow constraint problem.
- ;;
- ;; This always completes, as the graph is finite and the in and out sets
- ;; are complete semi-lattices. If the graph is reducible and the blocks
- ;; are sorted in reverse post-order, this completes in a maximum of LC +
- ;; 2 iterations, where LC is the loop connectedness number. See Hecht
- ;; and Ullman, "Analysis of a simple algorithm for global flow
- ;; problems", POPL 1973, or the recent summary in "Notes on graph
- ;; algorithms used in optimizing compilers", Offner 2013.
- (define (compute-maximum-fixed-point preds inv outv killv genv)
- (define (fold f seed l)
- (if (null? l) seed (fold f (f (car l) seed) (cdr l))))
- (let lp ((n 0) (changed? #f))
- (cond
- ((< n (vector-length preds))
- (let* ((in (vector-ref inv n))
- (in* (or
- (fold (lambda (pred set)
- (cond
- ((vector-ref outv pred)
- => (lambda (out)
- (if set
- (intset-union set out)
- out)))
- (else set)))
- in
- (vector-ref preds n))
- empty-intset)))
- (if (eq? in in*)
- (lp (1+ n) changed?)
- (let ((out* (fold (lambda (gen set)
- (intset-add set gen))
- (fold (lambda (kill set)
- (intset-remove set kill))
- in*
- (vector-ref killv n))
- (vector-ref genv n))))
- (vector-set! inv n in*)
- (vector-set! outv n out*)
- (lp (1+ n) #t)))))
- (changed?
- (lp 0 #f)))))
-
- (unless (and (= (vector-length (dfg-uses dfg)) (dfg-var-count dfg))
- (= (vector-length (dfg-cont-table dfg)) (dfg-label-count dfg)))
- (error "function needs renumbering"))
- (let* ((min-label (dfg-min-label dfg))
- (nlabels (dfg-label-count dfg))
- (min-var (dfg-min-var dfg))
- (nvars (dfg-var-count dfg))
- (usev (make-vector nlabels '()))
- (defv (make-vector nlabels '()))
- (live-in (make-vector nlabels #f))
- (live-out (make-vector nlabels #f)))
- (call-with-values
- (lambda ()
- (analyze-reverse-control-flow fun dfg min-label nlabels))
- (lambda (k-map succs)
- (define (var->idx var) (- var min-var))
- (define (idx->var idx) (+ idx min-var))
- (define (label->idx label)
- (vector-ref k-map (- label min-label)))
-
- ;; Initialize defv and usev.
- (let ((defs (dfg-defs dfg))
- (uses (dfg-uses dfg)))
- (let lp ((n 0))
- (when (< n (vector-length defs))
- (let ((def (vector-ref defs n)))
- (unless def
- (error "internal error -- var array not packed"))
- (for-each (lambda (def)
- (vector-push! defv (label->idx def) n))
- (lookup-predecessors def dfg))
- (for-each (lambda (use)
- (vector-push! usev (label->idx use) n))
- (vector-ref uses n))
- (lp (1+ n))))))
-
- ;; Liveness is a reverse data-flow problem, so we give
- ;; compute-maximum-fixed-point a reversed graph, swapping in for
- ;; out, usev for defv, and using successors instead of
- ;; predecessors. Continuation 0 is ktail.
- (compute-maximum-fixed-point succs live-out live-in defv usev)
-
- ;; Now rewrite the live-in and live-out sets to be indexed by
- ;; (LABEL - MIN-LABEL).
- (let ((live-in* (make-vector nlabels #f))
- (live-out* (make-vector nlabels #f)))
- (let lp ((idx 0))
- (when (< idx nlabels)
- (let ((dfa-idx (vector-ref k-map idx)))
- (vector-set! live-in* idx (vector-ref live-in dfa-idx))
- (vector-set! live-out* idx (vector-ref live-out dfa-idx))
- (lp (1+ idx)))))
-
- (make-dfa min-label min-var nvars live-in* live-out*))))))
-
-(define (print-dfa dfa)
- (match dfa
- (($ $dfa min-label min-var var-count in out)
- (define (print-var-set bv)
- (let lp ((n 0))
- (let ((n (intset-next bv n)))
- (when n
- (format #t " ~A" (+ n min-var))
- (lp (1+ n))))))
- (let lp ((n 0))
- (when (< n (vector-length in))
- (format #t "~A:\n" (+ n min-label))
- (format #t " in:")
- (print-var-set (vector-ref in n))
- (newline)
- (format #t " out:")
- (print-var-set (vector-ref out n))
- (newline)
- (lp (1+ n)))))))
-
-(define (compute-label-and-var-ranges fun global?)
- (define (min* a b)
- (if b (min a b) a))
- (define-syntax-rule (do-fold make-cont-folder)
- ((make-cont-folder min-label max-label label-count
- min-var max-var var-count)
- (lambda (label cont
- min-label max-label label-count
- min-var max-var var-count)
- (let ((min-label (min* label min-label))
- (max-label (max label max-label)))
- (match cont
- (($ $kargs names vars body)
- (values min-label max-label (1+ label-count)
- (cond (min-var (fold min min-var vars))
- ((pair? vars) (fold min (car vars) (cdr vars)))
- (else min-var))
- (fold max max-var vars)
- (+ var-count (length vars))))
- (($ $kfun src meta self)
- (values min-label max-label (1+ label-count)
- (min* self min-var) (max self max-var) (1+ var-count)))
- (_ (values min-label max-label (1+ label-count)
- min-var max-var var-count)))))
- fun
- #f -1 0 #f -1 0))
- (if global?
- (do-fold make-global-cont-folder)
- (do-fold make-local-cont-folder)))
-
-(define* (compute-dfg fun #:key (global? #t))
- (call-with-values (lambda () (compute-label-and-var-ranges fun global?))
- (lambda (min-label max-label label-count min-var max-var var-count)
- (when (or (zero? label-count) (zero? var-count))
- (error "internal error (no vars or labels for fun?)"))
- (let* ((nlabels (- (1+ max-label) min-label))
- (nvars (- (1+ max-var) min-var))
- (conts (make-vector nlabels #f))
- (preds (make-vector nlabels '()))
- (defs (make-vector nvars #f))
- (uses (make-vector nvars '()))
- (scopes (make-vector nlabels #f))
- (scope-levels (make-vector nlabels #f)))
- (define (var->idx var) (- var min-var))
- (define (label->idx label) (- label min-label))
-
- (define (add-def! var def-k)
- (vector-set! defs (var->idx var) def-k))
- (define (add-use! var use-k)
- (vector-push! uses (var->idx var) use-k))
-
- (define* (declare-block! label cont parent
- #:optional (level
- (1+ (vector-ref
- scope-levels
- (label->idx parent)))))
- (vector-set! conts (label->idx label) cont)
- (vector-set! scopes (label->idx label) parent)
- (vector-set! scope-levels (label->idx label) level))
-
- (define (link-blocks! pred succ)
- (vector-push! preds (label->idx succ) pred))
-
- (define (visit-cont cont label)
- (match cont
- (($ $kargs names syms body)
- (for-each (cut add-def! <> label) syms)
- (visit-term body label))
- (($ $kreceive arity k)
- (link-blocks! label k))))
-
- (define (visit-term term label)
- (match term
- (($ $letk (($ $cont k cont) ...) body)
- ;; Set up recursive environment before visiting cont bodies.
- (for-each/2 (lambda (cont k)
- (declare-block! k cont label))
- cont k)
- (for-each/2 visit-cont cont k)
- (visit-term body label))
- (($ $continue k src exp)
- (link-blocks! label k)
- (visit-exp exp label))))
-
- (define (visit-exp exp label)
- (define (use! sym)
- (add-use! sym label))
- (match exp
- ((or ($ $const) ($ $prim) ($ $closure)) #f)
- (($ $call proc args)
- (use! proc)
- (for-each use! args))
- (($ $callk k proc args)
- (use! proc)
- (for-each use! args))
- (($ $primcall name args)
- (for-each use! args))
- (($ $branch kt exp)
- (link-blocks! label kt)
- (visit-exp exp label))
- (($ $values args)
- (for-each use! args))
- (($ $prompt escape? tag handler)
- (use! tag)
- (link-blocks! label handler))
- (($ $fun body)
- (when global?
- (visit-fun body)))
- (($ $rec names syms funs)
- (unless global?
- (error "$rec should not be present when building a local DFG"))
- (for-each (lambda (fun)
- (match fun
- (($ $fun body)
- (visit-fun body))))
- funs))))
-
- (define (visit-clause clause kfun)
- (match clause
- (#f #t)
- (($ $cont kclause
- (and clause ($ $kclause arity ($ $cont kbody body)
- alternate)))
- (declare-block! kclause clause kfun)
- (link-blocks! kfun kclause)
-
- (declare-block! kbody body kclause)
- (link-blocks! kclause kbody)
-
- (visit-cont body kbody)
- (visit-clause alternate kfun))))
-
- (define (visit-fun fun)
- (match fun
- (($ $cont kfun
- (and cont
- ($ $kfun src meta self ($ $cont ktail tail) clause)))
- (declare-block! kfun cont #f 0)
- (add-def! self kfun)
- (declare-block! ktail tail kfun)
- (visit-clause clause kfun))))
-
- (visit-fun fun)
-
- (make-dfg conts preds defs uses scopes scope-levels
- min-label max-label label-count
- min-var max-var var-count)))))
-
-(define* (dump-dfg dfg #:optional (port (current-output-port)))
- (let ((min-label (dfg-min-label dfg))
- (min-var (dfg-min-var dfg)))
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
- (define (var->idx var) (- var min-var))
- (define (idx->var idx) (+ idx min-var))
-
- (let lp ((label (dfg-min-label dfg)))
- (when (<= label (dfg-max-label dfg))
- (let ((cont (vector-ref (dfg-cont-table dfg) (label->idx label))))
- (when cont
- (unless (equal? (lookup-predecessors label dfg) (list (1- label)))
- (newline port))
- (format port "k~a:~8t" label)
- (match cont
- (($ $kreceive arity k)
- (format port "$kreceive ~a k~a\n" arity k))
- (($ $kfun src meta self tail clause)
- (format port "$kfun ~a ~a v~a\n" src meta self))
- (($ $ktail)
- (format port "$ktail\n"))
- (($ $kclause arity ($ $cont kbody) alternate)
- (format port "$kclause ~a k~a" arity kbody)
- (match alternate
- (#f #f)
- (($ $cont kalt) (format port " -> k~a" kalt)))
- (newline port))
- (($ $kargs names vars term)
- (unless (null? vars)
- (format port "v~a[~a]~:{ v~a[~a]~}: "
- (car vars) (car names) (map list (cdr vars) (cdr names))))
- (match (find-call term)
- (($ $continue kf src ($ $branch kt exp))
- (format port "if ")
- (match exp
- (($ $primcall name args)
- (format port "(~a~{ v~a~})" name args))
- (($ $values (arg))
- (format port "v~a" arg)))
- (format port " k~a k~a\n" kt kf))
- (($ $continue k src exp)
- (match exp
- (($ $const val) (format port "const ~@y" val))
- (($ $prim name) (format port "prim ~a" name))
- (($ $fun ($ $cont kbody)) (format port "fun k~a" kbody))
- (($ $rec names syms funs) (format port "rec~{ v~a~}" syms))
- (($ $closure label nfree) (format port "closure k~a (~a free)" label nfree))
- (($ $call proc args) (format port "call~{ v~a~}" (cons proc args)))
- (($ $callk k proc args) (format port "callk k~a~{ v~a~}" k (cons proc args)))
- (($ $primcall name args) (format port "~a~{ v~a~}" name args))
- (($ $values args) (format port "values~{ v~a~}" args))
- (($ $prompt escape? tag handler) (format port "prompt ~a v~a k~a" escape? tag handler)))
- (unless (= k (1+ label))
- (format port " -> k~a" k))
- (newline port))))))
- (lp (1+ label)))))))
-
-(define-syntax-rule (with-fresh-name-state-from-dfg dfg body ...)
- (parameterize ((label-counter (1+ (dfg-max-label dfg)))
- (var-counter (1+ (dfg-max-var dfg))))
- body ...))
-
-(define (lookup-cont label dfg)
- (let ((res (vector-ref (dfg-cont-table dfg) (- label (dfg-min-label dfg)))))
- (unless res
- (error "Unknown continuation!" label))
- res))
-
-(define (lookup-predecessors k dfg)
- (vector-ref (dfg-preds dfg) (- k (dfg-min-label dfg))))
-
-(define (lookup-successors k dfg)
- (let ((cont (vector-ref (dfg-cont-table dfg) (- k (dfg-min-label dfg)))))
- (visit-cont-successors list cont)))
-
-(define (lookup-def var dfg)
- (vector-ref (dfg-defs dfg) (- var (dfg-min-var dfg))))
-
-(define (lookup-uses var dfg)
- (vector-ref (dfg-uses dfg) (- var (dfg-min-var dfg))))
-
-(define (lookup-block-scope k dfg)
- (vector-ref (dfg-scopes dfg) (- k (dfg-min-label dfg))))
-
-(define (lookup-scope-level k dfg)
- (vector-ref (dfg-scope-levels dfg) (- k (dfg-min-label dfg))))
-
-(define (find-defining-term sym dfg)
- (match (lookup-predecessors (lookup-def sym dfg) dfg)
- ((def-exp-k)
- (lookup-cont def-exp-k dfg))
- (else #f)))
-
-(define (find-call term)
- (match term
- (($ $kargs names syms body) (find-call body))
- (($ $letk conts body) (find-call body))
- (($ $continue) term)))
-
-(define (call-expression call)
- (match call
- (($ $continue k src exp) exp)))
-
-(define (find-expression term)
- (call-expression (find-call term)))
-
-(define (find-defining-expression sym dfg)
- (match (find-defining-term sym dfg)
- (#f #f)
- (($ $kreceive) #f)
- (($ $kclause) #f)
- (term (find-expression term))))
-
-(define (find-constant-value sym dfg)
- (match (find-defining-expression sym dfg)
- (($ $const val)
- (values #t val))
- (else
- (values #f #f))))
-
-(define (constant-needs-allocation? var val dfg)
- (define (immediate-u8? val)
- (and (integer? val) (exact? val) (<= 0 val 255)))
-
- (define (find-exp term)
- (match term
- (($ $kargs names vars body) (find-exp body))
- (($ $letk conts body) (find-exp body))
- (else term)))
-
- (or-map
- (lambda (use)
- (match (find-expression (lookup-cont use dfg))
- (($ $call) #f)
- (($ $callk) #f)
- (($ $values) #f)
- (($ $primcall 'free-ref (closure slot))
- (eq? var closure))
- (($ $primcall 'free-set! (closure slot value))
- (or (eq? var closure) (eq? var value)))
- (($ $primcall 'cache-current-module! (mod . _))
- (eq? var mod))
- (($ $primcall 'cached-toplevel-box _)
- #f)
- (($ $primcall 'cached-module-box _)
- #f)
- (($ $primcall 'resolve (name bound?))
- (eq? var name))
- (($ $primcall 'make-vector/immediate (len init))
- (eq? var init))
- (($ $primcall 'vector-ref/immediate (v i))
- (eq? var v))
- (($ $primcall 'vector-set!/immediate (v i x))
- (or (eq? var v) (eq? var x)))
- (($ $primcall 'allocate-struct/immediate (vtable nfields))
- (eq? var vtable))
- (($ $primcall 'struct-ref/immediate (s n))
- (eq? var s))
- (($ $primcall 'struct-set!/immediate (s n x))
- (or (eq? var s) (eq? var x)))
- (($ $primcall 'builtin-ref (idx))
- #f)
- (_ #t)))
- (vector-ref (dfg-uses dfg) (- var (dfg-min-var dfg)))))
-
-(define (continuation-scope-contains? scope-k k dfg)
- (let ((scope-level (lookup-scope-level scope-k dfg)))
- (let lp ((k k))
- (or (eq? scope-k k)
- (and (< scope-level (lookup-scope-level k dfg))
- (lp (lookup-block-scope k dfg)))))))
-
-(define (continuation-bound-in? k use-k dfg)
- (continuation-scope-contains? (lookup-block-scope k dfg) use-k dfg))
-
-(define (variable-free-in? var k dfg)
- (or-map (lambda (use)
- (continuation-scope-contains? k use dfg))
- (lookup-uses var dfg)))
-
-;; A continuation is a control point if it has multiple predecessors, or
-;; if its single predecessor does not have a single successor.
-(define (control-point? k dfg)
- (match (lookup-predecessors k dfg)
- ((pred)
- (let ((cont (vector-ref (dfg-cont-table dfg)
- (- pred (dfg-min-label dfg)))))
- (visit-cont-successors (case-lambda
- (() #t)
- ((succ0) #f)
- ((succ1 succ2) #t))
- cont)))
- (_ #t)))
-
-(define (lookup-bound-syms k dfg)
- (match (lookup-cont k dfg)
- (($ $kargs names syms body)
- syms)))
diff --git a/module/language/cps/renumber.scm b/module/language/cps/renumber.scm
deleted file mode 100644
index 8a1c7a0f2..000000000
--- a/module/language/cps/renumber.scm
+++ /dev/null
@@ -1,343 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2014, 2015 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:
-;;;
-;;; A pass to renumber variables and continuation labels so that they
-;;; are contiguous within each function and, in the case of labels,
-;;; topologically sorted.
-;;;
-;;; Code:
-
-(define-module (language cps renumber)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (language cps)
- #:export (renumber))
-
-;; Topologically sort the continuation tree starting at k0, using
-;; reverse post-order numbering.
-(define (sort-conts k0 conts new-k0 path-lengths)
- (let ((next -1))
- (let visit ((k k0))
- (define (maybe-visit k)
- (let ((entry (vector-ref conts k)))
- ;; Visit the successor if it has not been
- ;; visited yet.
- (when (and entry (not (exact-integer? entry)))
- (visit k))))
-
- (let ((cont (vector-ref conts k)))
- ;; Clear the cont table entry to mark this continuation as
- ;; visited.
- (vector-set! conts k #f)
-
- (match cont
- (($ $kargs names syms body)
- (let lp ((body body))
- (match body
- (($ $letk conts body) (lp body))
- (($ $continue k src exp)
- (match exp
- (($ $prompt escape? tag handler)
- (maybe-visit handler)
- (maybe-visit k))
- (($ $branch kt)
- ;; Visit the successor with the shortest path length
- ;; to the tail first, so that if the branches are
- ;; unsorted, the longer path length will appear
- ;; first. This will move a loop exit out of a loop.
- (let ((k-len (vector-ref path-lengths k))
- (kt-len (vector-ref path-lengths kt)))
- (cond
- ((if kt-len
- (or (not k-len)
- (< k-len kt-len)
- ;; If the path lengths are the
- ;; same, preserve original order
- ;; to avoid squirreliness.
- (and (= k-len kt-len) (< kt k)))
- (if k-len #f (< kt k)))
- (maybe-visit k)
- (maybe-visit kt))
- (else
- (maybe-visit kt)
- (maybe-visit k)))))
- (_
- (maybe-visit k)))))))
- (($ $kreceive arity k) (maybe-visit k))
- (($ $kclause arity ($ $cont kbody) alt)
- (match alt
- (($ $cont kalt) (maybe-visit kalt))
- (_ #f))
- (maybe-visit kbody))
- (($ $kfun src meta self tail clause)
- (match clause
- (($ $cont kclause) (maybe-visit kclause))
- (_ #f)))
- (_ #f))
-
- ;; Chain this label to the label that will follow it in the sort
- ;; order, and record this label as the new head of the order.
- (vector-set! conts k next)
- (set! next k)))
-
- ;; Finally traverse the label chain, giving each label its final
- ;; name.
- (let lp ((n new-k0) (head next))
- (if (< head 0)
- n
- (let ((next (vector-ref conts head)))
- (vector-set! conts head n)
- (lp (1+ n) next))))))
-
-(define (compute-tail-path-lengths preds ktail path-lengths)
- (let visit ((k ktail) (length-in 0))
- (let ((length (vector-ref path-lengths k)))
- (unless (and length (<= length length-in))
- (vector-set! path-lengths k length-in)
- (let lp ((preds (vector-ref preds k)))
- (match preds
- (() #t)
- ((pred . preds)
- (visit pred (1+ length-in))
- (lp preds))))))))
-
-(define (compute-new-labels-and-vars fun)
- (call-with-values (lambda () (compute-max-label-and-var fun))
- (lambda (max-label max-var)
- (let ((labels (make-vector (1+ max-label) #f))
- (next-label 0)
- (vars (make-vector (1+ max-var) #f))
- (next-var 0)
- (preds (make-vector (1+ max-label) '()))
- (path-lengths (make-vector (1+ max-label) #f)))
- (define (add-predecessor! pred succ)
- (vector-set! preds succ (cons pred (vector-ref preds succ))))
- (define (rename! var)
- (vector-set! vars var next-var)
- (set! next-var (1+ next-var)))
-
- (define (collect-conts fun)
- (define (visit-cont cont)
- (match cont
- (($ $cont label cont)
- (vector-set! labels label cont)
- (match cont
- (($ $kargs names vars body)
- (visit-term body label))
- (($ $kfun src meta self tail clause)
- (visit-cont tail)
- (match clause
- (($ $cont kclause)
- (add-predecessor! label kclause)
- (visit-cont clause))
- (#f #f)))
- (($ $kclause arity (and body ($ $cont kbody)) alternate)
- (add-predecessor! label kbody)
- (visit-cont body)
- (match alternate
- (($ $cont kalt)
- (add-predecessor! label kalt)
- (visit-cont alternate))
- (#f #f)))
- (($ $kreceive arity kargs)
- (add-predecessor! label kargs))
- (($ $ktail) #f)))))
- (define (visit-term term label)
- (match term
- (($ $letk conts body)
- (let lp ((conts conts))
- (unless (null? conts)
- (visit-cont (car conts))
- (lp (cdr conts))))
- (visit-term body label))
- (($ $continue k src exp)
- (add-predecessor! label k)
- (match exp
- (($ $branch kt)
- (add-predecessor! label kt))
- (($ $prompt escape? tag handler)
- (add-predecessor! label handler))
- (_ #f)))))
- (visit-cont fun))
-
- (define (compute-names-in-fun fun)
- (define queue '())
- (define (visit-cont cont)
- (match cont
- (($ $cont label cont)
- (let ((reachable? (exact-integer? (vector-ref labels label))))
- ;; This cont is reachable if it was given a number.
- ;; Otherwise the cont table entry still contains the
- ;; cont itself; clear it out to indicate that the cont
- ;; should not be residualized.
- (unless reachable?
- (vector-set! labels label #f))
- (match cont
- (($ $kargs names vars body)
- (when reachable?
- (for-each rename! vars))
- (visit-term body reachable?))
- (($ $kfun src meta self tail clause)
- (unless reachable? (error "entry should be reachable"))
- (rename! self)
- (visit-cont tail)
- (when clause
- (visit-cont clause)))
- (($ $kclause arity body alternate)
- (unless reachable? (error "clause should be reachable"))
- (visit-cont body)
- (when alternate
- (visit-cont alternate)))
- (($ $ktail)
- (unless reachable?
- ;; It's possible for the tail to be unreachable,
- ;; if all paths contify to infinite loops. Make
- ;; sure we mark as reachable.
- (vector-set! labels label next-label)
- (set! next-label (1+ next-label))))
- (($ $kreceive)
- #f))))))
- (define (visit-term term reachable?)
- (match term
- (($ $letk conts body)
- (for-each visit-cont conts)
- (visit-term body reachable?))
- (($ $continue k src ($ $fun body))
- (when reachable?
- (set! queue (cons body queue))))
- (($ $continue k src ($ $rec names syms funs))
- (when reachable?
- (set! queue (fold (lambda (fun queue)
- (match fun
- (($ $fun body)
- (cons body queue))))
- queue
- funs))))
- (($ $continue) #f)))
-
- (match fun
- (($ $cont kfun ($ $kfun src meta self ($ $cont ktail)))
- (collect-conts fun)
- (compute-tail-path-lengths preds ktail path-lengths)
- (set! next-label (sort-conts kfun labels next-label path-lengths))
- (visit-cont fun)
- (for-each compute-names-in-fun (reverse queue)))
- (($ $program conts)
- (for-each compute-names-in-fun conts))))
-
- (compute-names-in-fun fun)
- (values labels vars next-label next-var)))))
-
-(define (apply-renumbering term labels vars)
- (define (relabel label) (vector-ref labels label))
- (define (rename var) (vector-ref vars var))
- (define (rename-kw-arity arity)
- (match arity
- (($ $arity req opt rest kw aok?)
- (make-$arity req opt rest
- (map (match-lambda
- ((kw kw-name kw-var)
- (list kw kw-name (rename kw-var))))
- kw)
- aok?))))
- (define (must-visit-cont cont)
- (or (visit-cont cont)
- (error "internal error -- failed to visit cont")))
- (define (visit-conts conts)
- (match conts
- (() '())
- ((cont . conts)
- (cond
- ((visit-cont cont)
- => (lambda (cont)
- (cons cont (visit-conts conts))))
- (else (visit-conts conts))))))
- (define (visit-cont cont)
- (match cont
- (($ $cont label cont)
- (let ((label (relabel label)))
- (and
- label
- (rewrite-cps-cont cont
- (($ $kargs names vars body)
- (label ($kargs names (map rename vars) ,(visit-term body))))
- (($ $kfun src meta self tail clause)
- (label
- ($kfun src meta (rename self) ,(must-visit-cont tail)
- ,(and clause (must-visit-cont clause)))))
- (($ $ktail)
- (label ($ktail)))
- (($ $kclause arity body alternate)
- (label
- ($kclause ,(rename-kw-arity arity) ,(must-visit-cont body)
- ,(and alternate (must-visit-cont alternate)))))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (label ($kreceive req rest (relabel kargs))))))))))
- (define (visit-term term)
- (rewrite-cps-term term
- (($ $letk conts body)
- ,(match (visit-conts conts)
- (() (visit-term body))
- (conts (build-cps-term ($letk ,conts ,(visit-term body))))))
- (($ $continue k src exp)
- ($continue (relabel k) src ,(visit-exp exp)))))
- (define (visit-exp exp)
- (match exp
- ((or ($ $const) ($ $prim))
- exp)
- (($ $closure k nfree)
- (build-cps-exp ($closure (relabel k) nfree)))
- (($ $fun)
- (visit-fun exp))
- (($ $rec names vars funs)
- (build-cps-exp ($rec names (map rename vars) (map visit-fun funs))))
- (($ $values args)
- (let ((args (map rename args)))
- (build-cps-exp ($values args))))
- (($ $call proc args)
- (let ((args (map rename args)))
- (build-cps-exp ($call (rename proc) args))))
- (($ $callk k proc args)
- (let ((args (map rename args)))
- (build-cps-exp ($callk (relabel k) (rename proc) args))))
- (($ $branch kt exp)
- (build-cps-exp ($branch (relabel kt) ,(visit-exp exp))))
- (($ $primcall name args)
- (let ((args (map rename args)))
- (build-cps-exp ($primcall name args))))
- (($ $prompt escape? tag handler)
- (build-cps-exp
- ($prompt escape? (rename tag) (relabel handler))))))
- (define (visit-fun fun)
- (rewrite-cps-exp fun
- (($ $fun body)
- ($fun ,(must-visit-cont body)))))
-
- (match term
- (($ $cont)
- (must-visit-cont term))
- (($ $program conts)
- (build-cps-term
- ($program ,(map must-visit-cont conts))))))
-
-(define (renumber term)
- (call-with-values (lambda () (compute-new-labels-and-vars term))
- (lambda (labels vars nlabels nvars)
- (values (apply-renumbering term labels vars) nlabels nvars))))
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
deleted file mode 100644
index c60f0f22f..000000000
--- a/module/language/cps/slot-allocation.scm
+++ /dev/null
@@ -1,689 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A module to assign stack slots to variables in a CPS term.
-;;;
-;;; Code:
-
-(define-module (language cps slot-allocation)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:use-module (language cps dfg)
- #:use-module (language cps intset)
- #:export (allocate-slots
- lookup-slot
- lookup-maybe-slot
- lookup-constant-value
- lookup-maybe-constant-value
- lookup-nlocals
- lookup-call-proc-slot
- lookup-parallel-moves
- lookup-dead-slot-map))
-
-(define-record-type $allocation
- (make-allocation dfa slots
- has-constv constant-values
- call-allocations
- nlocals)
- allocation?
-
- ;; A DFA records all variables bound in a function, and assigns them
- ;; indices. The slot in which a variable is stored at runtime can be
- ;; had by indexing into the SLOTS vector with the variable's index.
- ;;
- (dfa allocation-dfa)
- (slots allocation-slots)
-
- ;; Not all variables have slots allocated. Variables that are
- ;; constant and that are only used by primcalls that can accept
- ;; constants directly are not allocated to slots, and their SLOT value
- ;; is false. Likewise constants that are only used by calls are not
- ;; allocated into slots, to avoid needless copying. If a variable is
- ;; constant, its constant value is set in the CONSTANT-VALUES vector
- ;; and the corresponding bit in the HAS-CONSTV bitvector is set.
- ;;
- (has-constv allocation-has-constv)
- (constant-values allocation-constant-values)
-
- ;; Some continuations have additional associated information. This
- ;; addition information is a /call allocation/. Call allocations
- ;; record the way that functions are passed values, and how their
- ;; return values are rebound to local variables.
- ;;
- ;; A call allocation contains three pieces of information: the call's
- ;; /proc slot/, a set of /parallel moves/, and a /dead slot map/. The
- ;; proc slot indicates the slot of a procedure in a procedure call, or
- ;; where the procedure would be in a multiple-value return. The
- ;; parallel moves shuffle locals into position for a call, or shuffle
- ;; returned values back into place. Though they use the same slot,
- ;; moves for a call are called "call moves", and moves to handle a
- ;; return are "return moves". The dead slot map indicates, for a
- ;; call, what slots should be ignored by GC when marking the frame.
- ;;
- ;; $kreceive continuations record a proc slot and a set of return moves
- ;; to adapt multiple values from the stack to local variables.
- ;;
- ;; Tail calls record arg moves, but no proc slot.
- ;;
- ;; Non-tail calls record arg moves, a call slot, and a dead slot map.
- ;; Multiple-valued returns will have an associated $kreceive
- ;; continuation, which records the same proc slot, but has return
- ;; moves and no dead slot map.
- ;;
- ;; $prompt handlers are $kreceive continuations like any other.
- ;;
- ;; $values expressions with more than 1 value record moves but have no
- ;; proc slot or dead slot map.
- ;;
- ;; A set of moves is expressed as an ordered list of (SRC . DST)
- ;; moves, where SRC and DST are slots. This may involve a temporary
- ;; variable. A dead slot map is a bitfield, as an integer.
- ;;
- (call-allocations allocation-call-allocations)
-
- ;; The number of locals for a $kclause.
- ;;
- (nlocals allocation-nlocals))
-
-(define-record-type $call-allocation
- (make-call-allocation proc-slot moves dead-slot-map)
- call-allocation?
- (proc-slot call-allocation-proc-slot)
- (moves call-allocation-moves)
- (dead-slot-map call-allocation-dead-slot-map))
-
-(define (find-first-zero n)
- ;; Naive implementation.
- (let lp ((slot 0))
- (if (logbit? slot n)
- (lp (1+ slot))
- slot)))
-
-(define (find-first-trailing-zero n)
- (let lp ((slot (let lp ((count 2))
- (if (< n (ash 1 (1- count)))
- count
- ;; Grow upper bound slower than factor 2 to avoid
- ;; needless bignum allocation on 32-bit systems
- ;; when there are more than 16 locals.
- (lp (+ count (ash count -1)))))))
- (if (or (zero? slot) (logbit? (1- slot) n))
- slot
- (lp (1- slot)))))
-
-(define (lookup-maybe-slot sym allocation)
- (match allocation
- (($ $allocation dfa slots)
- (vector-ref slots (dfa-var-idx dfa sym)))))
-
-(define (lookup-slot sym allocation)
- (or (lookup-maybe-slot sym allocation)
- (error "Variable not allocated to a slot" sym)))
-
-(define (lookup-constant-value sym allocation)
- (match allocation
- (($ $allocation dfa slots has-constv constant-values)
- (let ((idx (dfa-var-idx dfa sym)))
- (if (bitvector-ref has-constv idx)
- (vector-ref constant-values idx)
- (error "Variable does not have constant value" sym))))))
-
-(define (lookup-maybe-constant-value sym allocation)
- (match allocation
- (($ $allocation dfa slots has-constv constant-values)
- (let ((idx (dfa-var-idx dfa sym)))
- (values (bitvector-ref has-constv idx)
- (vector-ref constant-values idx))))))
-
-(define (lookup-call-allocation k allocation)
- (or (hashq-ref (allocation-call-allocations allocation) k)
- (error "Continuation not a call" k)))
-
-(define (lookup-call-proc-slot k allocation)
- (or (call-allocation-proc-slot (lookup-call-allocation k allocation))
- (error "Call has no proc slot" k)))
-
-(define (lookup-parallel-moves k allocation)
- (or (call-allocation-moves (lookup-call-allocation k allocation))
- (error "Call has no use parallel moves slot" k)))
-
-(define (lookup-dead-slot-map k allocation)
- (or (call-allocation-dead-slot-map (lookup-call-allocation k allocation))
- (error "Call has no dead slot map" k)))
-
-(define (lookup-nlocals k allocation)
- (or (hashq-ref (allocation-nlocals allocation) k)
- (error "Not a clause continuation" k)))
-
-(define (solve-parallel-move src dst tmp)
- "Solve the parallel move problem between src and dst slot lists, which
-are comparable with eqv?. A tmp slot may be used."
-
- ;; This algorithm is taken from: "Tilting at windmills with Coq:
- ;; formal verification of a compilation algorithm for parallel moves"
- ;; by Laurence Rideau, Bernard Paul Serpette, and Xavier Leroy
- ;;
-
- (define (split-move moves reg)
- (let loop ((revhead '()) (tail moves))
- (match tail
- (((and s+d (s . d)) . rest)
- (if (eqv? s reg)
- (cons d (append-reverse revhead rest))
- (loop (cons s+d revhead) rest)))
- (_ #f))))
-
- (define (replace-last-source reg moves)
- (match moves
- ((moves ... (s . d))
- (append moves (list (cons reg d))))))
-
- (let loop ((to-move (map cons src dst))
- (being-moved '())
- (moved '())
- (last-source #f))
- ;; 'last-source' should always be equivalent to:
- ;; (and (pair? being-moved) (car (last being-moved)))
- (match being-moved
- (() (match to-move
- (() (reverse moved))
- (((and s+d (s . d)) . t1)
- (if (or (eqv? s d) ; idempotent
- (not s)) ; src is a constant and can be loaded directly
- (loop t1 '() moved #f)
- (loop t1 (list s+d) moved s)))))
- (((and s+d (s . d)) . b)
- (match (split-move to-move d)
- ((r . t1) (loop t1 (acons d r being-moved) moved last-source))
- (#f (match b
- (() (loop to-move '() (cons s+d moved) #f))
- (_ (if (eqv? d last-source)
- (loop to-move
- (replace-last-source tmp b)
- (cons s+d (acons d tmp moved))
- tmp)
- (loop to-move b (cons s+d moved) last-source))))))))))
-
-(define (dead-after-def? k-idx v-idx dfa)
- (not (intset-ref (dfa-k-in dfa k-idx) v-idx)))
-
-(define (dead-after-use? k-idx v-idx dfa)
- (not (intset-ref (dfa-k-out dfa k-idx) v-idx)))
-
-(define (allocate-slots fun dfg)
- (let* ((dfa (compute-live-variables fun dfg))
- (min-label (dfg-min-label dfg))
- (label-count (dfg-label-count dfg))
- (usev (make-vector label-count '()))
- (defv (make-vector label-count '()))
- (slots (make-vector (dfa-var-count dfa) #f))
- (constant-values (make-vector (dfa-var-count dfa) #f))
- (has-constv (make-bitvector (dfa-var-count dfa) #f))
- (has-slotv (make-bitvector (dfa-var-count dfa) #t))
- (needs-slotv (make-bitvector (dfa-var-count dfa) #t))
- (needs-hintv (make-bitvector (dfa-var-count dfa) #f))
- (call-allocations (make-hash-table))
- (nlocals 0) ; Mutable. It pains me.
- (nlocals-table (make-hash-table)))
-
- (define (label->idx label) (- label min-label))
- (define (idx->label idx) (+ idx min-label))
-
- (define (bump-nlocals! nlocals*)
- (when (< nlocals nlocals*)
- (set! nlocals nlocals*)))
-
- (define (empty-live-slots)
- #b0)
-
- (define (add-live-slot slot live-slots)
- (logior live-slots (ash 1 slot)))
-
- (define (kill-dead-slot slot live-slots)
- (logand live-slots (lognot (ash 1 slot))))
-
- (define (compute-slot live-slots hint)
- ;; Slots 253-255 are reserved for shuffling; see comments in
- ;; assembler.scm.
- (if (and hint (not (logbit? hint live-slots))
- (or (< hint 253) (> hint 255)))
- hint
- (let ((slot (find-first-zero live-slots)))
- (if (or (< slot 253) (> slot 255))
- slot
- (+ 256 (find-first-zero (ash live-slots -256)))))))
-
- (define (compute-call-proc-slot live-slots)
- (+ 2 (find-first-trailing-zero live-slots)))
-
- (define (compute-prompt-handler-proc-slot live-slots)
- (if (zero? live-slots)
- 0
- (1- (find-first-trailing-zero live-slots))))
-
- (define (recompute-live-slots k)
- (let ((in (dfa-k-in dfa (label->idx k))))
- (let lp ((v 0) (live-slots 0))
- (let ((v (intset-next in v)))
- (if v
- (let ((slot (vector-ref slots v)))
- (lp (1+ v)
- (if slot
- (add-live-slot slot live-slots)
- live-slots)))
- live-slots)))))
-
- (define* (allocate! var-idx hint live)
- (cond
- ((not (bitvector-ref needs-slotv var-idx)) live)
- ((vector-ref slots var-idx) => (cut add-live-slot <> live))
- ((and (not hint) (bitvector-ref needs-hintv var-idx)) live)
- (else
- (let ((slot (compute-slot live hint)))
- (bump-nlocals! (1+ slot))
- (vector-set! slots var-idx slot)
- (add-live-slot slot live)))))
-
- ;; Although some parallel moves may proceed without a temporary
- ;; slot, in general one is needed. That temporary slot must not be
- ;; part of the source or destination sets, and that slot should not
- ;; correspond to a live variable. Usually the source and
- ;; destination sets are a subset of the union of the live sets
- ;; before and after the move. However for stack slots that don't
- ;; have names -- those slots that correspond to function arguments
- ;; or to function return values -- it could be that they are out of
- ;; the computed live set. In that case they need to be adjoined to
- ;; the live set, used when choosing a temporary slot.
- ;;
- ;; Note that although we reserve slots 253-255 for shuffling
- ;; operands that address less than the full 24-bit range of locals,
- ;; that reservation doesn't apply here, because this temporary
- ;; itself is used while doing parallel assignment via "mov", and
- ;; "mov" does not need shuffling.
- (define (compute-tmp-slot live stack-slots)
- (find-first-zero (fold add-live-slot live stack-slots)))
-
- (define (parallel-move src-slots dst-slots tmp-slot)
- (let ((moves (solve-parallel-move src-slots dst-slots tmp-slot)))
- (when (assv tmp-slot moves)
- (bump-nlocals! (1+ tmp-slot)))
- moves))
-
- ;; Find variables that are actually constant, and determine which
- ;; of those can avoid slot allocation.
- (define (compute-constants!)
- (let lp ((n 0))
- (when (< n (vector-length constant-values))
- (let ((sym (dfa-var-sym dfa n)))
- (call-with-values (lambda () (find-constant-value sym dfg))
- (lambda (has-const? const)
- (when has-const?
- (bitvector-set! has-constv n has-const?)
- (vector-set! constant-values n const)
- (when (not (constant-needs-allocation? sym const dfg))
- (bitvector-set! needs-slotv n #f)))
- (lp (1+ n))))))))
-
- ;; Record uses and defs, as lists of variable indexes, indexed by
- ;; label index.
- (define (compute-uses-and-defs!)
- (let lp ((n 0))
- (when (< n (vector-length usev))
- (match (lookup-cont (idx->label n) dfg)
- (($ $kfun src meta self)
- (vector-set! defv n (list (dfa-var-idx dfa self))))
- (($ $kargs names syms body)
- (vector-set! defv n (map (cut dfa-var-idx dfa <>) syms))
- (vector-set! usev n
- (map (cut dfa-var-idx dfa <>)
- (match (find-expression body)
- (($ $call proc args)
- (cons proc args))
- (($ $callk k proc args)
- (cons proc args))
- (($ $primcall name args)
- args)
- (($ $branch kt ($ $primcall name args))
- args)
- (($ $branch kt ($ $values args))
- args)
- (($ $values args)
- args)
- (($ $prompt escape? tag handler)
- (list tag))
- (_ '())))))
- (_ #f))
- (lp (1+ n)))))
-
- ;; Results of function calls that are not used don't need to be
- ;; allocated to slots.
- (define (compute-unused-results!)
- (define (kreceive-get-kargs kreceive)
- (match (lookup-cont kreceive dfg)
- (($ $kreceive arity kargs) kargs)
- (_ #f)))
- (let ((candidates (make-bitvector label-count #f)))
- ;; Find all $kargs that are the successors of $kreceive nodes.
- (let lp ((n 0))
- (when (< n label-count)
- (and=> (kreceive-get-kargs (idx->label n))
- (lambda (kargs)
- (bitvector-set! candidates (label->idx kargs) #t)))
- (lp (1+ n))))
- ;; For $kargs that only have $kreceive predecessors, remove unused
- ;; variables from the needs-slotv set.
- (let lp ((n 0))
- (let ((n (bit-position #t candidates n)))
- (when n
- (match (lookup-predecessors (idx->label n) dfg)
- ;; At least one kreceive is in the predecessor set, so we
- ;; only need to do the check for nodes with >1
- ;; predecessor.
- ((or (_) ((? kreceive-get-kargs) ...))
- (for-each (lambda (var)
- (when (dead-after-def? n var dfa)
- (bitvector-set! needs-slotv var #f)))
- (vector-ref defv n)))
- (_ #f))
- (lp (1+ n)))))))
-
- ;; Compute the set of variables whose allocation should be delayed
- ;; until a "hint" is known about where to allocate them. This is
- ;; the case for some procedure arguments.
- ;;
- ;; This algorithm used is a conservative approximation of what
- ;; really should happen, which would be eager allocation of call
- ;; frames as soon as it's known that a call will happen. It would
- ;; be nice to recast this as a proper data-flow problem.
- (define (compute-needs-hint!)
- (define (live-before n)
- (dfa-k-in dfa n))
- (define (live-after n)
- (dfa-k-out dfa n))
- (define needs-slot
- (bitvector->intset needs-slotv))
-
- ;; Walk backwards. At a call, compute the set of variables that
- ;; have allocated slots and are live before but not after. This
- ;; set contains candidates for needs-hintv.
- (define (scan-for-call n)
- (when (<= 0 n)
- (match (lookup-cont (idx->label n) dfg)
- (($ $kargs names syms body)
- (match (find-expression body)
- ((or ($ $call) ($ $callk))
- (let* ((args (intset-subtract (live-before n) (live-after n)))
- (args-needing-slots (intset-intersect args needs-slot)))
- (if (intset-next args-needing-slots #f)
- (scan-for-hints (1- n) args-needing-slots)
- (scan-for-call (1- n)))))
- (_ (scan-for-call (1- n)))))
- (_ (scan-for-call (1- n))))))
-
- ;; Walk backwards in the current basic block. Stop when the block
- ;; ends, we reach a call, or when an expression kills a value.
- (define (scan-for-hints n args)
- (when (< 0 n)
- (match (lookup-cont (idx->label n) dfg)
- (($ $kargs names syms body)
- (match (lookup-predecessors (idx->label (1+ n)) dfg)
- (((? (cut eqv? <> (idx->label n))))
- ;; If we are indeed in the same basic block, then if we
- ;; are finished with the scan, we kill uses of the
- ;; terminator, but leave its definitions.
- (match (find-expression body)
- ((or ($ $const) ($ $prim) ($ $closure)
- ($ $primcall) ($ $prompt)
- ;; If $values has more than one argument, it may
- ;; use a temporary, which would invalidate our
- ;; assumptions that slots not allocated are not
- ;; used.
- ($ $values (or () (_))))
- (define (intset-empty? intset) (not (intset-next intset)))
- (let ((killed (intset-subtract (live-before n) (live-after n))))
- ;; If the expression kills no values needing slots,
- ;; and defines no value needing a slot that's not
- ;; in our args, then we keep on trucking.
- (if (intset-empty? (intset-intersect
- (fold (lambda (def clobber)
- (if (intset-ref args def)
- clobber
- (intset-add clobber def)))
- killed
- (vector-ref defv n))
- needs-slot))
- (scan-for-hints (1- n) args)
- (finish-hints n (live-before n) args))))
- ((or ($ $call) ($ $callk) ($ $values) ($ $branch))
- (finish-hints n (live-before n) args))))
- ;; Otherwise we kill uses of the block entry.
- (_ (finish-hints n (live-before (1+ n)) args))))
- (_ (finish-hints n (live-before (1+ n)) args)))))
-
- ;; Add definitions ARGS minus KILL to NEED-HINTS, and go back to
- ;; looking for calls.
- (define (finish-hints n kill args)
- (let ((new-hints (intset-subtract args kill)))
- (let lp ((n 0))
- (let ((n (intset-next new-hints n)))
- (when n
- (bitvector-set! needs-hintv n #t)
- (lp (1+ n))))))
- (scan-for-call n))
-
- (scan-for-call (1- label-count)))
-
- (define (allocate-call label k uses pre-live post-live)
- (match (lookup-cont k dfg)
- (($ $ktail)
- (let* ((tail-nlocals (length uses))
- (tail-slots (iota tail-nlocals))
- (pre-live (fold allocate! pre-live uses tail-slots))
- (moves (parallel-move (map (cut vector-ref slots <>) uses)
- tail-slots
- (compute-tmp-slot pre-live tail-slots))))
- (bump-nlocals! tail-nlocals)
- (hashq-set! call-allocations label
- (make-call-allocation #f moves #f))))
- (($ $kreceive arity kargs)
- (let* ((proc-slot (compute-call-proc-slot post-live))
- (call-slots (map (cut + proc-slot <>) (iota (length uses))))
- (pre-live (fold allocate! pre-live uses call-slots))
- (arg-moves (parallel-move (map (cut vector-ref slots <>) uses)
- call-slots
- (compute-tmp-slot pre-live
- call-slots)))
- (result-vars (vector-ref defv (label->idx kargs)))
- (value-slots (map (cut + proc-slot 1 <>)
- (iota (length result-vars))))
- ;; Shuffle the first result down to the lowest slot, and
- ;; leave any remaining results where they are. This
- ;; strikes a balance between avoiding shuffling,
- ;; especially for unused extra values, and avoiding
- ;; frame size growth due to sparse locals.
- (result-live (match (cons result-vars value-slots)
- ((() . ()) post-live)
- (((var . vars) . (slot . slots))
- (fold allocate!
- (allocate! var #f post-live)
- vars slots))))
- (result-slots (map (cut vector-ref slots <>) result-vars))
- ;; Filter out unused results.
- (value-slots (filter-map (lambda (val result) (and result val))
- value-slots result-slots))
- (result-slots (filter (lambda (x) x) result-slots))
- (result-moves (parallel-move value-slots
- result-slots
- (compute-tmp-slot result-live
- value-slots)))
- (dead-slot-map (logand (1- (ash 1 (- proc-slot 2)))
- (lognot post-live))))
- (bump-nlocals! (+ proc-slot (length uses)))
- (hashq-set! call-allocations label
- (make-call-allocation proc-slot arg-moves dead-slot-map))
- (hashq-set! call-allocations k
- (make-call-allocation proc-slot result-moves #f))))))
-
- (define (allocate-values label k uses pre-live post-live)
- (match (lookup-cont k dfg)
- (($ $ktail)
- (let* ((src-slots (map (cut vector-ref slots <>) uses))
- (tail-nlocals (1+ (length uses)))
- (dst-slots (cdr (iota tail-nlocals)))
- (moves (parallel-move src-slots dst-slots
- (compute-tmp-slot pre-live dst-slots))))
- (bump-nlocals! tail-nlocals)
- (hashq-set! call-allocations label
- (make-call-allocation #f moves #f))))
- (($ $kargs (_) (_))
- ;; When there is only one value in play, we allow the dst to be
- ;; hinted (see scan-for-hints). If the src doesn't have a
- ;; slot, then the actual slot for the dst would end up being
- ;; decided by the call that uses it. Because we don't know the
- ;; slot, we can't really compute the parallel moves in that
- ;; case, so just bail and rely on the bytecode emitter to
- ;; handle the one-value case specially.
- (match (cons uses (vector-ref defv (label->idx k)))
- (((src) . (dst))
- (allocate! dst (vector-ref slots src) post-live))))
- (($ $kargs)
- (let* ((src-slots (map (cut vector-ref slots <>) uses))
- (dst-vars (vector-ref defv (label->idx k)))
- (result-live (fold allocate! post-live dst-vars src-slots))
- (dst-slots (map (cut vector-ref slots <>) dst-vars))
- (moves (parallel-move src-slots dst-slots
- (compute-tmp-slot (logior pre-live result-live)
- '()))))
- (hashq-set! call-allocations label
- (make-call-allocation #f moves #f))))))
-
- (define (allocate-prompt label k handler)
- (match (lookup-cont handler dfg)
- (($ $kreceive arity kargs)
- (let* ((handler-live (recompute-live-slots handler))
- (proc-slot (compute-prompt-handler-proc-slot handler-live))
- (result-vars (vector-ref defv (label->idx kargs)))
- (value-slots (map (cut + proc-slot 1 <>)
- (iota (length result-vars))))
- (result-live (fold allocate!
- handler-live result-vars value-slots))
- (result-slots (map (cut vector-ref slots <>) result-vars))
- ;; Filter out unused results.
- (value-slots (filter-map (lambda (val result) (and result val))
- value-slots result-slots))
- (result-slots (filter (lambda (x) x) result-slots))
- (moves (parallel-move value-slots
- result-slots
- (compute-tmp-slot result-live
- value-slots))))
- (bump-nlocals! (+ proc-slot 1 (length result-vars)))
- (hashq-set! call-allocations handler
- (make-call-allocation proc-slot moves #f))))))
-
- (define (allocate-defs! n live)
- (fold (cut allocate! <> #f <>) live (vector-ref defv n)))
-
- ;; This traversal will visit definitions before uses, as
- ;; definitions dominate uses and a block's dominator will appear
- ;; before it, in reverse post-order.
- (define (visit-clause n live)
- (let lp ((n n) (live (recompute-live-slots (idx->label n))))
- (define (kill-dead live vars-by-label-idx pred)
- (fold (lambda (v live)
- (let ((slot (vector-ref slots v)))
- (if (and slot (pred n v dfa))
- (kill-dead-slot slot live)
- live)))
- live
- (vector-ref vars-by-label-idx n)))
- (define (kill-dead-defs live)
- (kill-dead live defv dead-after-def?))
- (define (kill-dead-uses live)
- (kill-dead live usev dead-after-use?))
- (if (= n label-count)
- n
- (let* ((label (idx->label n))
- (live (if (control-point? label dfg)
- (recompute-live-slots label)
- live))
- (live (kill-dead-defs (allocate-defs! n live)))
- (post-live (kill-dead-uses live)))
- ;; LIVE are the live slots coming into the term.
- ;; POST-LIVE is the subset that is still live after the
- ;; term uses its inputs.
- (match (lookup-cont (idx->label n) dfg)
- (($ $kclause) n)
- (($ $kargs names syms body)
- (define (compute-k-live k)
- (match (lookup-predecessors k dfg)
- ((_) post-live)
- (_ (recompute-live-slots k))))
- (let ((uses (vector-ref usev n)))
- (match (find-call body)
- (($ $continue k src (or ($ $call) ($ $callk)))
- (allocate-call label k uses live (compute-k-live k)))
- (($ $continue k src ($ $primcall)) #t)
- (($ $continue k src ($ $values))
- (allocate-values label k uses live (compute-k-live k)))
- (($ $continue k src ($ $prompt escape? tag handler))
- (allocate-prompt label k handler))
- (_ #f)))
- (lp (1+ n) post-live))
- ((or ($ $kreceive) ($ $ktail))
- (lp (1+ n) post-live)))))))
-
- (define (visit-entry)
- (define (visit-clauses n live)
- (unless (eqv? live (add-live-slot 0 (empty-live-slots)))
- (error "Unexpected clause live set"))
- (set! nlocals 1)
- (match (lookup-cont (idx->label n) dfg)
- (($ $kclause arity ($ $cont kbody ($ $kargs names)) alternate)
- (unless (eq? (idx->label (1+ n)) kbody)
- (error "Unexpected label order"))
- (let* ((nargs (length names))
- (next (visit-clause (1+ n)
- (fold allocate! live
- (vector-ref defv (1+ n))
- (cdr (iota (1+ nargs)))))))
- (hashq-set! nlocals-table (idx->label n) nlocals)
- (when (< next label-count)
- (match alternate
- (($ $cont kalt)
- (unless (eq? kalt (idx->label next))
- (error "Unexpected clause order"))))
- (visit-clauses next live))))))
- (match (lookup-cont (idx->label 0) dfg)
- (($ $kfun src meta self)
- (visit-clauses 1 (allocate-defs! 0 (empty-live-slots))))))
-
- (compute-constants!)
- (compute-uses-and-defs!)
- (compute-unused-results!)
- (compute-needs-hint!)
- (visit-entry)
-
- (make-allocation dfa slots
- has-constv constant-values
- call-allocations
- nlocals-table)))
diff --git a/module/language/cps/spec.scm b/module/language/cps/spec.scm
deleted file mode 100644
index f1255afeb..000000000
--- a/module/language/cps/spec.scm
+++ /dev/null
@@ -1,37 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013 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
-
-;;; Code:
-
-(define-module (language cps spec)
- #:use-module (system base language)
- #:use-module (language cps)
- #:use-module (language cps compile-bytecode)
- #:export (cps))
-
-(define* (write-cps exp #:optional (port (current-output-port)))
- (write (unparse-cps exp) port))
-
-(define-language cps
- #:title "CPS Intermediate Language"
- #:reader (lambda (port env) (read port))
- #:printer write-cps
- #:parser parse-cps
- #:compilers `((bytecode . ,compile-bytecode))
- #:for-humans? #f
- )
diff --git a/module/language/cps/verify.scm b/module/language/cps/verify.scm
deleted file mode 100644
index 6c2310737..000000000
--- a/module/language/cps/verify.scm
+++ /dev/null
@@ -1,195 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;;
-;;; Code:
-
-(define-module (language cps verify)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-26)
- #:use-module (language cps)
- #:export (verify-cps))
-
-(define (verify-cps fun)
- (define seen-labels (make-hash-table))
- (define seen-vars (make-hash-table))
-
- (define (add sym seen env)
- (when (hashq-ref seen sym)
- (error "duplicate gensym" sym))
- (hashq-set! seen sym #t)
- (cons sym env))
-
- (define (add-env new seen env)
- (if (null? new)
- env
- (add-env (cdr new) seen (add (car new) seen env))))
-
- (define (add-vars new env)
- (unless (and-map exact-integer? new)
- (error "bad vars" new))
- (add-env new seen-vars env))
-
- (define (add-labels new env)
- (unless (and-map exact-integer? new)
- (error "bad labels" new))
- (add-env new seen-labels env))
-
- (define (check-ref sym seen env)
- (cond
- ((not (hashq-ref seen sym))
- (error "unbound lexical" sym))
- ((not (memq sym env))
- (error "displaced lexical" sym))))
-
- (define (check-label sym env)
- (check-ref sym seen-labels env))
-
- (define (check-var sym env)
- (check-ref sym seen-vars env))
-
- (define (check-src src)
- (if (and src (not (and (list? src) (and-map pair? src)
- (and-map symbol? (map car src)))))
- (error "bad src")))
-
- (define (visit-cont-body cont k-env v-env)
- (match cont
- (($ $kreceive ($ $arity ((? symbol?) ...) () (or #f (? symbol?)) () #f) k)
- (check-label k k-env))
- (($ $kargs (name ...) (sym ...) body)
- (unless (= (length name) (length sym))
- (error "name and sym lengths don't match" name sym))
- (visit-term body k-env (add-vars sym v-env)))
- (_
- ;; $kclause, $kfun, and $ktail are only ever seen in $fun.
- (error "unexpected cont body" cont))))
-
- (define (visit-clause clause k-env v-env)
- (match clause
- (($ $cont kclause
- ($ $kclause
- ($ $arity
- ((? symbol? req) ...)
- ((? symbol? opt) ...)
- (and rest (or #f (? symbol?)))
- (((? keyword? kw) (? symbol? kwname) kwsym) ...)
- (or #f #t))
- ($ $cont kbody (and body ($ $kargs names syms _)))
- alternate))
- (for-each (lambda (sym)
- (unless (memq sym syms)
- (error "bad keyword sym" sym)))
- kwsym)
- ;; FIXME: It is technically possible for kw syms to alias other
- ;; syms.
- (unless (equal? (append req opt (if rest (list rest) '()) kwname)
- names)
- (error "clause body names do not match arity names" exp))
- (let ((k-env (add-labels (list kclause kbody) k-env)))
- (visit-cont-body body k-env v-env))
- (when alternate
- (visit-clause alternate k-env v-env)))
- (_
- (error "unexpected clause" clause))))
-
- (define (visit-entry entry k-env v-env)
- (match entry
- (($ $cont kbody
- ($ $kfun src meta self ($ $cont ktail ($ $ktail)) clause))
- (when (and meta (not (and (list? meta) (and-map pair? meta))))
- (error "meta should be alist" meta))
- (check-src src)
- ;; Reset the continuation environment, because Guile's
- ;; continuations are local.
- (let ((v-env (add-vars (list self) v-env))
- (k-env (add-labels (list ktail) '())))
- (when clause
- (visit-clause clause k-env v-env))))
- (_ (error "unexpected $kfun" entry))))
-
- (define (visit-fun fun k-env v-env)
- (match fun
- (($ $fun entry)
- (visit-entry entry '() v-env))
- (_
- (error "unexpected $fun" fun))))
-
- (define (visit-expression exp k-env v-env)
- (match exp
- (($ $const val)
- #t)
- (($ $prim (? symbol? name))
- #t)
- (($ $closure kfun n)
- #t)
- (($ $fun)
- (visit-fun exp k-env v-env))
- (($ $rec (name ...) (sym ...) (fun ...))
- (unless (= (length name) (length sym) (length fun))
- (error "letrec syms, names, and funs not same length" term))
- ;; FIXME: syms added in two places (here in $rec versus also in
- ;; target $kargs)
- (let ((v-env (add-vars sym v-env)))
- (for-each (cut visit-fun <> k-env v-env) fun)))
- (($ $call proc (arg ...))
- (check-var proc v-env)
- (for-each (cut check-var <> v-env) arg))
- (($ $callk k* proc (arg ...))
- ;; We don't check that k* is in scope; it's actually inside some
- ;; other function, probably. We rely on the transformation that
- ;; introduces the $callk to be correct, and the linker to resolve
- ;; the reference.
- (check-var proc v-env)
- (for-each (cut check-var <> v-env) arg))
- (($ $branch kt ($ $primcall (? symbol? name) (arg ...)))
- (check-var kt k-env)
- (for-each (cut check-var <> v-env) arg))
- (($ $branch kt ($ $values (arg ...)))
- (check-var kt k-env)
- (for-each (cut check-var <> v-env) arg))
- (($ $primcall (? symbol? name) (arg ...))
- (for-each (cut check-var <> v-env) arg))
- (($ $values (arg ...))
- (for-each (cut check-var <> v-env) arg))
- (($ $prompt escape? tag handler)
- (unless (boolean? escape?) (error "escape? should be boolean" escape?))
- (check-var tag v-env)
- (check-label handler k-env))
- (_
- (error "unexpected expression" exp))))
-
- (define (visit-term term k-env v-env)
- (match term
- (($ $letk (($ $cont k cont) ...) body)
- (let ((k-env (add-labels k k-env)))
- (for-each (cut visit-cont-body <> k-env v-env) cont)
- (visit-term body k-env v-env)))
-
- (($ $continue k src exp)
- (check-label k k-env)
- (check-src src)
- (visit-expression exp k-env v-env))
-
- (_
- (error "unexpected term" term))))
-
- (visit-entry fun '() '())
- fun)
diff --git a/module/language/cps2/compile-cps.scm b/module/language/cps2/compile-cps.scm
deleted file mode 100644
index ee6e3d5bb..000000000
--- a/module/language/cps2/compile-cps.scm
+++ /dev/null
@@ -1,129 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Compiling CPS2 to CPS. When/if CPS2 replaces CPS, this module will be removed.
-;;;
-;;; Code:
-
-(define-module (language cps2 compile-cps)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module ((language cps) #:prefix cps:)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 closure-conversion)
- #:use-module (language cps2 optimize)
- #:use-module (language cps2 reify-primitives)
- #:use-module (language cps2 renumber)
- #:use-module (language cps intmap)
- #:export (compile-cps))
-
-;; Precondition: For each function in CONTS, the continuation names are
-;; topologically sorted.
-(define* (conts->fun conts #:optional (kfun 0))
- (define (convert-fun kfun)
- (let ((doms (compute-dom-edges (compute-idoms conts kfun))))
- (define (visit-cont label)
- (cps:rewrite-cps-cont (intmap-ref conts label)
- (($ $kargs names syms body)
- (label (cps:$kargs names syms ,(redominate label (visit-term body)))))
- (($ $ktail)
- (label (cps:$ktail)))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (label (cps:$kreceive req rest kargs)))))
- (define (visit-clause label)
- (and label
- (cps:rewrite-cps-cont (intmap-ref conts label)
- (($ $kclause ($ $arity req opt rest kw aok?) kbody kalt)
- (label (cps:$kclause (req opt rest kw aok?)
- ,(visit-cont kbody)
- ,(visit-clause kalt)))))))
- (define (redominate label term)
- (define (visit-dom-conts label)
- (match (intmap-ref conts label)
- (($ $ktail) '())
- (($ $kargs) (list (visit-cont label)))
- (else
- (cons (visit-cont label)
- (visit-dom-conts* (intmap-ref doms label))))))
- (define (visit-dom-conts* labels)
- (match labels
- (() '())
- ((label . labels)
- (append (visit-dom-conts label)
- (visit-dom-conts* labels)))))
- (cps:rewrite-cps-term (visit-dom-conts* (intmap-ref doms label))
- (() ,term)
- (conts (cps:$letk ,conts ,term))))
- (define (visit-term term)
- (cps:rewrite-cps-term term
- (($ $continue k src (and ($ $fun) fun))
- (cps:$continue k src ,(visit-fun fun)))
- (($ $continue k src ($ $rec names syms funs))
- (cps:$continue k src (cps:$rec names syms (map visit-fun funs))))
- (($ $continue k src exp)
- (cps:$continue k src ,(visit-exp exp)))))
- (define (visit-exp exp)
- (cps:rewrite-cps-exp exp
- (($ $const val) (cps:$const val))
- (($ $prim name) (cps:$prim name))
- (($ $closure k nfree) (cps:$closure k nfree))
- (($ $call proc args) (cps:$call proc args))
- (($ $callk k proc args) (cps:$callk k proc args))
- (($ $primcall name args) (cps:$primcall name args))
- (($ $branch k exp) (cps:$branch k ,(visit-exp exp)))
- (($ $values args) (cps:$values args))
- (($ $prompt escape? tag handler) (cps:$prompt escape? tag handler))))
- (define (visit-fun fun)
- (cps:rewrite-cps-exp fun
- (($ $fun body)
- (cps:$fun ,(convert-fun body)))))
-
- (cps:rewrite-cps-cont (intmap-ref conts kfun)
- (($ $kfun src meta self tail clause)
- (kfun (cps:$kfun src meta self (tail (cps:$ktail))
- ,(visit-clause clause)))))))
- (convert-fun kfun))
-
-(define (conts->fun* conts)
- (cps:build-cps-term
- (cps:$program
- ,(intmap-fold-right (lambda (label cont out)
- (match cont
- (($ $kfun)
- (cons (conts->fun conts label) out))
- (_ out)))
- conts
- '()))))
-
-(define (kw-arg-ref args kw default)
- (match (memq kw args)
- ((_ val . _) val)
- (_ default)))
-
-(define (compile-cps exp env opts)
- ;; Use set! to save memory at bootstrap-time. (The interpreter holds
- ;; onto all free variables locally bound in a function, so if we used
- ;; let*, we'd hold onto earlier copies of the term.)
- (set! exp (optimize-higher-order-cps exp opts))
- (set! exp (convert-closures exp))
- (set! exp (optimize-first-order-cps exp opts))
- (set! exp (reify-primitives exp))
- (set! exp (renumber exp))
- (values (conts->fun* exp) env env))
From aa7f0e25ac8ea8340745f6aa337a9f0c64f00881 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 17:18:30 +0200
Subject: [PATCH 032/865] Rename CPS2 to CPS
---
module/Makefile.am | 57 +-
module/language/cps2.scm | 362 -----
module/language/cps2/closure-conversion.scm | 824 ----------
module/language/cps2/compile-bytecode.scm | 433 -----
module/language/cps2/constructors.scm | 98 --
module/language/cps2/contification.scm | 475 ------
module/language/cps2/cse.scm | 449 ------
module/language/cps2/dce.scm | 399 -----
module/language/cps2/effects-analysis.scm | 484 ------
module/language/cps2/elide-values.scm | 88 --
module/language/cps2/optimize.scm | 106 --
module/language/cps2/prune-bailouts.scm | 86 -
.../language/cps2/prune-top-level-scopes.scm | 63 -
module/language/cps2/reify-primitives.scm | 167 --
module/language/cps2/renumber.scm | 217 ---
module/language/cps2/self-references.scm | 79 -
module/language/cps2/simplify.scm | 267 ----
module/language/cps2/slot-allocation.scm | 995 ------------
module/language/cps2/spec.scm | 37 -
module/language/cps2/specialize-primcalls.scm | 59 -
module/language/cps2/split-rec.scm | 174 --
module/language/cps2/type-fold.scm | 425 -----
module/language/cps2/types.scm | 1408 -----------------
module/language/cps2/utils.scm | 477 ------
module/language/cps2/verify.scm | 306 ----
module/language/cps2/with-cps.scm | 145 --
.../{compile-cps2.scm => compile-cps.scm} | 12 +-
module/language/tree-il/spec.scm | 6 +-
28 files changed, 36 insertions(+), 8662 deletions(-)
delete mode 100644 module/language/cps2.scm
delete mode 100644 module/language/cps2/closure-conversion.scm
delete mode 100644 module/language/cps2/compile-bytecode.scm
delete mode 100644 module/language/cps2/constructors.scm
delete mode 100644 module/language/cps2/contification.scm
delete mode 100644 module/language/cps2/cse.scm
delete mode 100644 module/language/cps2/dce.scm
delete mode 100644 module/language/cps2/effects-analysis.scm
delete mode 100644 module/language/cps2/elide-values.scm
delete mode 100644 module/language/cps2/optimize.scm
delete mode 100644 module/language/cps2/prune-bailouts.scm
delete mode 100644 module/language/cps2/prune-top-level-scopes.scm
delete mode 100644 module/language/cps2/reify-primitives.scm
delete mode 100644 module/language/cps2/renumber.scm
delete mode 100644 module/language/cps2/self-references.scm
delete mode 100644 module/language/cps2/simplify.scm
delete mode 100644 module/language/cps2/slot-allocation.scm
delete mode 100644 module/language/cps2/spec.scm
delete mode 100644 module/language/cps2/specialize-primcalls.scm
delete mode 100644 module/language/cps2/split-rec.scm
delete mode 100644 module/language/cps2/type-fold.scm
delete mode 100644 module/language/cps2/types.scm
delete mode 100644 module/language/cps2/utils.scm
delete mode 100644 module/language/cps2/verify.scm
delete mode 100644 module/language/cps2/with-cps.scm
rename module/language/tree-il/{compile-cps2.scm => compile-cps.scm} (99%)
diff --git a/module/Makefile.am b/module/Makefile.am
index c53f9e466..b29a4bf00 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -60,7 +60,6 @@ SOURCES = \
\
language/tree-il.scm \
$(TREE_IL_LANG_SOURCES) \
- $(CPS2_LANG_SOURCES) \
$(CPS_LANG_SOURCES) \
$(BYTECODE_LANG_SOURCES) \
$(VALUE_LANG_SOURCES) \
@@ -117,39 +116,37 @@ TREE_IL_LANG_SOURCES = \
language/tree-il/canonicalize.scm \
language/tree-il/analyze.scm \
language/tree-il/inline.scm \
- language/tree-il/compile-cps2.scm \
+ language/tree-il/compile-cps.scm \
language/tree-il/debug.scm \
language/tree-il/spec.scm
CPS_LANG_SOURCES = \
- language/cps/primitives.scm
-
-CPS2_LANG_SOURCES = \
- language/cps2.scm \
- language/cps2/closure-conversion.scm \
- language/cps2/compile-bytecode.scm \
- language/cps2/constructors.scm \
- language/cps2/contification.scm \
- language/cps2/cse.scm \
- language/cps2/dce.scm \
- language/cps2/effects-analysis.scm \
- language/cps2/elide-values.scm \
- language/cps2/prune-bailouts.scm \
- language/cps2/prune-top-level-scopes.scm \
- language/cps2/reify-primitives.scm \
- language/cps2/renumber.scm \
- language/cps2/optimize.scm \
- language/cps2/simplify.scm \
- language/cps2/self-references.scm \
- language/cps2/slot-allocation.scm \
- language/cps2/spec.scm \
- language/cps2/specialize-primcalls.scm \
- language/cps2/split-rec.scm \
- language/cps2/type-fold.scm \
- language/cps2/types.scm \
- language/cps2/utils.scm \
- language/cps2/verify.scm \
- language/cps2/with-cps.scm
+ language/cps.scm \
+ language/cps/closure-conversion.scm \
+ language/cps/compile-bytecode.scm \
+ language/cps/constructors.scm \
+ language/cps/contification.scm \
+ language/cps/cse.scm \
+ language/cps/dce.scm \
+ language/cps/effects-analysis.scm \
+ language/cps/elide-values.scm \
+ language/cps/primitives.scm \
+ language/cps/prune-bailouts.scm \
+ language/cps/prune-top-level-scopes.scm \
+ language/cps/reify-primitives.scm \
+ language/cps/renumber.scm \
+ language/cps/optimize.scm \
+ language/cps/simplify.scm \
+ language/cps/self-references.scm \
+ language/cps/slot-allocation.scm \
+ language/cps/spec.scm \
+ language/cps/specialize-primcalls.scm \
+ language/cps/split-rec.scm \
+ language/cps/type-fold.scm \
+ language/cps/types.scm \
+ language/cps/utils.scm \
+ language/cps/verify.scm \
+ language/cps/with-cps.scm
BYTECODE_LANG_SOURCES = \
language/bytecode.scm \
diff --git a/module/language/cps2.scm b/module/language/cps2.scm
deleted file mode 100644
index 76219f376..000000000
--- a/module/language/cps2.scm
+++ /dev/null
@@ -1,362 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; [Transitional note: CPS2 is a new version of CPS, and is a bit of an
-;;; experiment. All of the comments in this file pretend that CPS2 will
-;;; replace CPS, and will be named CPS.]
-;;;
-;;; This is the continuation-passing style (CPS) intermediate language
-;;; (IL) for Guile.
-;;;
-;;; In CPS, a term is a labelled expression that calls a continuation.
-;;; A function is a collection of terms. No term belongs to more than
-;;; one function. The function is identified by the label of its entry
-;;; term, and its body is composed of those terms that are reachable
-;;; from the entry term. A program is a collection of functions,
-;;; identified by the entry label of the entry function.
-;;;
-;;; Terms are themselves wrapped in continuations, which specify how
-;;; predecessors may continue to them. For example, a $kargs
-;;; continuation specifies that the term may be called with a specific
-;;; number of values, and that those values will then be bound to
-;;; lexical variables. $kreceive specifies that some number of values
-;;; will be passed on the stack, as from a multiple-value return. Those
-;;; values will be passed to a $kargs, if the number of values is
-;;; compatible with the $kreceive's arity. $kfun is an entry point to a
-;;; function, and receives arguments according to a well-known calling
-;;; convention (currently, on the stack) and the stack before
-;;; dispatching to a $kclause. A $kclause is a case-lambda clause, and
-;;; only appears within a $kfun; it checks the incoming values for the
-;;; correct arity and dispatches to a $kargs, or to the next clause.
-;;; Finally, $ktail is the tail continuation for a function, and
-;;; contains no term.
-;;;
-;;; Each continuation has a label that is unique in the program. As an
-;;; implementation detail, the labels are integers, which allows us to
-;;; easily sort them topologically. A program is a map from integers to
-;;; continuations, where continuation 0 in the map is the entry point
-;;; for the program, and is a $kfun of no arguments.
-;;;
-;;; $continue nodes call continuations. The expression contained in the
-;;; $continue node determines the value or values that are passed to the
-;;; target continuation: $const to pass a constant value, $values to
-;;; pass multiple named values, etc. $continue nodes also record the
-;;; source location corresponding to the expression.
-;;;
-;;; As mentioned above, a $kargs continuation can bind variables, if it
-;;; receives incoming values. $kfun also binds a value, corresponding
-;;; to the closure being called. A traditional CPS implementation will
-;;; nest terms in each other, binding them in "let" forms, ensuring that
-;;; continuations are declared and bound within the scope of the values
-;;; that they may use. In this way, the scope tree is a proof that
-;;; variables are defined before they are used. However, this proof is
-;;; conservative; it is possible for a variable to always be defined
-;;; before it is used, but not to be in scope:
-;;;
-;;; (letrec ((k1 (lambda (v1) (k2)))
-;;; (k2 (lambda () v1)))
-;;; (k1 0))
-;;;
-;;; This example is invalid, as v1 is used outside its scope. However
-;;; it would be perfectly fine for k2 to use v1 if k2 were nested inside
-;;; k1:
-;;;
-;;; (letrec ((k1 (lambda (v1)
-;;; (letrec ((k2 (lambda () v1)))
-;;; (k2))))
-;;; (k1 0))
-;;;
-;;; Because program transformation usually uses flow-based analysis,
-;;; having to update the scope tree to manifestly prove a transformation
-;;; that has already proven correct is needless overhead, and in the
-;;; worst case can prevent optimizations from occuring. For that
-;;; reason, Guile's CPS language does not nest terms. Instead, we use
-;;; the invariant that definitions must dominate uses. To check the
-;;; validity of a CPS program is thus more involved than checking for a
-;;; well-scoped tree; you have to do flow analysis to determine a
-;;; dominator tree. However the flexibility that this grants us is
-;;; worth the cost of throwing away the embedded proof of the scope
-;;; tree.
-;;;
-;;; This particular formulation of CPS was inspired by Andrew Kennedy's
-;;; 2007 paper, "Compiling with Continuations, Continued". All Guile
-;;; hackers should read that excellent paper! As in Kennedy's paper,
-;;; continuations are second-class, and may be thought of as basic block
-;;; labels. All values are bound to variables using continuation calls:
-;;; even constants!
-;;;
-;;; Finally, note that there are two flavors of CPS: higher-order and
-;;; first-order. By "higher-order", we mean that variables may be free
-;;; across function boundaries. Higher-order CPS contains $fun and $rec
-;;; expressions that declare functions in the scope of their term.
-;;; Closure conversion results in first-order CPS, where closure
-;;; representations have been explicitly chosen, and all variables used
-;;; in a function are bound. Higher-order CPS is good for
-;;; interprocedural optimizations like contification and beta reduction,
-;;; while first-order CPS is better for instruction selection, register
-;;; allocation, and code generation.
-;;;
-;;; See (language tree-il compile-cps) for details on how Tree-IL
-;;; converts to CPS.
-;;;
-;;; Code:
-
-(define-module (language cps2)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-9 gnu)
- #:use-module (srfi srfi-11)
- #:export (;; Helper.
- $arity
- make-$arity
-
- ;; Continuations.
- $kreceive $kargs $kfun $ktail $kclause
-
- ;; Terms.
- $continue
-
- ;; Expressions.
- $const $prim $fun $rec $closure $branch
- $call $callk $primcall $values $prompt
-
- ;; Building macros.
- build-cont build-term build-exp
- rewrite-cont rewrite-term rewrite-exp
-
- ;; External representation.
- parse-cps unparse-cps))
-
-;; FIXME: Use SRFI-99, when Guile adds it.
-(define-syntax define-record-type*
- (lambda (x)
- (define (id-append ctx . syms)
- (datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
- (syntax-case x ()
- ((_ name field ...)
- (and (identifier? #'name) (and-map identifier? #'(field ...)))
- (with-syntax ((cons (id-append #'name #'make- #'name))
- (pred (id-append #'name #'name #'?))
- ((getter ...) (map (lambda (f)
- (id-append f #'name #'- f))
- #'(field ...))))
- #'(define-record-type name
- (cons field ...)
- pred
- (field getter)
- ...))))))
-
-(define-syntax-rule (define-cps-type name field ...)
- (begin
- (define-record-type* name field ...)
- (set-record-type-printer! name print-cps)))
-
-(define (print-cps exp port)
- (format port "#" (unparse-cps exp)))
-
-;; Helper.
-(define-record-type* $arity req opt rest kw allow-other-keys?)
-
-;; Continuations
-(define-cps-type $kreceive arity kbody)
-(define-cps-type $kargs names syms term)
-(define-cps-type $kfun src meta self ktail kclause)
-(define-cps-type $ktail)
-(define-cps-type $kclause arity kbody kalternate)
-
-;; Terms.
-(define-cps-type $continue k src exp)
-
-;; Expressions.
-(define-cps-type $const val)
-(define-cps-type $prim name)
-(define-cps-type $fun body) ; Higher-order.
-(define-cps-type $rec names syms funs) ; Higher-order.
-(define-cps-type $closure label nfree) ; First-order.
-(define-cps-type $branch kt exp)
-(define-cps-type $call proc args)
-(define-cps-type $callk k proc args) ; First-order.
-(define-cps-type $primcall name args)
-(define-cps-type $values args)
-(define-cps-type $prompt escape? tag handler)
-
-(define-syntax build-arity
- (syntax-rules (unquote)
- ((_ (unquote exp)) exp)
- ((_ (req opt rest kw allow-other-keys?))
- (make-$arity req opt rest kw allow-other-keys?))))
-
-(define-syntax build-cont
- (syntax-rules (unquote $kreceive $kargs $kfun $ktail $kclause)
- ((_ (unquote exp))
- exp)
- ((_ ($kreceive req rest kargs))
- (make-$kreceive (make-$arity req '() rest '() #f) kargs))
- ((_ ($kargs (name ...) (unquote syms) body))
- (make-$kargs (list name ...) syms (build-term body)))
- ((_ ($kargs (name ...) (sym ...) body))
- (make-$kargs (list name ...) (list sym ...) (build-term body)))
- ((_ ($kargs names syms body))
- (make-$kargs names syms (build-term body)))
- ((_ ($kfun src meta self ktail kclause))
- (make-$kfun src meta self ktail kclause))
- ((_ ($ktail))
- (make-$ktail))
- ((_ ($kclause arity kbody kalternate))
- (make-$kclause (build-arity arity) kbody kalternate))))
-
-(define-syntax build-term
- (syntax-rules (unquote $rec $continue)
- ((_ (unquote exp))
- exp)
- ((_ ($continue k src exp))
- (make-$continue k src (build-exp exp)))))
-
-(define-syntax build-exp
- (syntax-rules (unquote
- $const $prim $fun $rec $closure $branch
- $call $callk $primcall $values $prompt)
- ((_ (unquote exp)) exp)
- ((_ ($const val)) (make-$const val))
- ((_ ($prim name)) (make-$prim name))
- ((_ ($fun kentry)) (make-$fun kentry))
- ((_ ($rec names gensyms funs)) (make-$rec names gensyms funs))
- ((_ ($closure k nfree)) (make-$closure k nfree))
- ((_ ($call proc (unquote args))) (make-$call proc args))
- ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
- ((_ ($call proc args)) (make-$call proc args))
- ((_ ($callk k proc (unquote args))) (make-$callk k proc args))
- ((_ ($callk k proc (arg ...))) (make-$callk k proc (list arg ...)))
- ((_ ($callk k proc args)) (make-$callk k proc args))
- ((_ ($primcall name (unquote args))) (make-$primcall name args))
- ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
- ((_ ($primcall name args)) (make-$primcall name args))
- ((_ ($values (unquote args))) (make-$values args))
- ((_ ($values (arg ...))) (make-$values (list arg ...)))
- ((_ ($values args)) (make-$values args))
- ((_ ($branch kt exp)) (make-$branch kt (build-exp exp)))
- ((_ ($prompt escape? tag handler))
- (make-$prompt escape? tag handler))))
-
-(define-syntax-rule (rewrite-cont x (pat cont) ...)
- (match x
- (pat (build-cont cont)) ...))
-(define-syntax-rule (rewrite-term x (pat term) ...)
- (match x
- (pat (build-term term)) ...))
-(define-syntax-rule (rewrite-exp x (pat body) ...)
- (match x
- (pat (build-exp body)) ...))
-
-(define (parse-cps exp)
- (define (src exp)
- (let ((props (source-properties exp)))
- (and (pair? props) props)))
- (match exp
- ;; Continuations.
- (('kreceive req rest k)
- (build-cont ($kreceive req rest k)))
- (('kargs names syms body)
- (build-cont ($kargs names syms ,(parse-cps body))))
- (('kfun src meta self ktail kclause)
- (build-cont ($kfun (src exp) meta self ktail kclause)))
- (('ktail)
- (build-cont ($ktail)))
- (('kclause (req opt rest kw allow-other-keys?) kbody)
- (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody #f)))
- (('kclause (req opt rest kw allow-other-keys?) kbody kalt)
- (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody kalt)))
-
- ;; Calls.
- (('continue k exp)
- (build-term ($continue k (src exp) ,(parse-cps exp))))
- (('unspecified)
- (build-exp ($const *unspecified*)))
- (('const exp)
- (build-exp ($const exp)))
- (('prim name)
- (build-exp ($prim name)))
- (('fun kbody)
- (build-exp ($fun kbody)))
- (('closure k nfree)
- (build-exp ($closure k nfree)))
- (('rec (name sym fun) ...)
- (build-exp ($rec name sym (map parse-cps fun))))
- (('call proc arg ...)
- (build-exp ($call proc arg)))
- (('callk k proc arg ...)
- (build-exp ($callk k proc arg)))
- (('primcall name arg ...)
- (build-exp ($primcall name arg)))
- (('branch k exp)
- (build-exp ($branch k ,(parse-cps exp))))
- (('values arg ...)
- (build-exp ($values arg)))
- (('prompt escape? tag handler)
- (build-exp ($prompt escape? tag handler)))
- (_
- (error "unexpected cps" exp))))
-
-(define (unparse-cps exp)
- (match exp
- ;; Continuations.
- (($ $kreceive ($ $arity req () rest () #f) k)
- `(kreceive ,req ,rest ,k))
- (($ $kargs names syms body)
- `(kargs ,names ,syms ,(unparse-cps body)))
- (($ $kfun src meta self ktail kclause)
- `(kfun ,meta ,self ,ktail ,kclause))
- (($ $ktail)
- `(ktail))
- (($ $kclause ($ $arity req opt rest kw allow-other-keys?) kbody kalternate)
- `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,kbody
- . ,(if kalternate (list kalternate) '())))
-
- ;; Calls.
- (($ $continue k src exp)
- `(continue ,k ,(unparse-cps exp)))
- (($ $const val)
- (if (unspecified? val)
- '(unspecified)
- `(const ,val)))
- (($ $prim name)
- `(prim ,name))
- (($ $fun kbody)
- `(fun ,kbody))
- (($ $closure k nfree)
- `(closure ,k ,nfree))
- (($ $rec names syms funs)
- `(rec ,@(map (lambda (name sym fun)
- (list name sym (unparse-cps fun)))
- names syms funs)))
- (($ $call proc args)
- `(call ,proc ,@args))
- (($ $callk k proc args)
- `(callk ,k ,proc ,@args))
- (($ $primcall name args)
- `(primcall ,name ,@args))
- (($ $branch k exp)
- `(branch ,k ,(unparse-cps exp)))
- (($ $values args)
- `(values ,@args))
- (($ $prompt escape? tag handler)
- `(prompt ,escape? ,tag ,handler))
- (_
- (error "unexpected cps" exp))))
diff --git a/module/language/cps2/closure-conversion.scm b/module/language/cps2/closure-conversion.scm
deleted file mode 100644
index 7de34482f..000000000
--- a/module/language/cps2/closure-conversion.scm
+++ /dev/null
@@ -1,824 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; This pass converts a CPS term in such a way that no function has any
-;;; free variables. Instead, closures are built explicitly with
-;;; make-closure primcalls, and free variables are referenced through
-;;; the closure.
-;;;
-;;; Closure conversion also removes any $rec expressions that
-;;; contification did not handle. See (language cps) for a further
-;;; discussion of $rec.
-;;;
-;;; Code:
-
-(define-module (language cps2 closure-conversion)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold
- filter-map
- ))
- #:use-module (srfi srfi-11)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (convert-closures))
-
-(define (compute-function-bodies conts kfun)
- "Compute a map from FUN-LABEL->BODY-LABEL... for all $fun instances in
-conts."
- (let visit-fun ((kfun kfun) (out empty-intmap))
- (let ((body (compute-function-body conts kfun)))
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
- (visit-fun kfun out))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
- (fold visit-fun out kfun))
- (_ out)))
- body
- (intmap-add out kfun body)))))
-
-(define (compute-program-body functions)
- (intmap-fold (lambda (label body out) (intset-union body out))
- functions
- empty-intset))
-
-(define (filter-reachable conts functions)
- (let ((reachable (compute-program-body functions)))
- (intmap-fold
- (lambda (label cont out)
- (if (intset-ref reachable label)
- out
- (intmap-remove out label)))
- conts conts)))
-
-(define (compute-non-operator-uses conts)
- (persistent-intset
- (intmap-fold
- (lambda (label cont uses)
- (define (add-use var uses) (intset-add! uses var))
- (define (add-uses vars uses)
- (match vars
- (() uses)
- ((var . vars) (add-uses vars (add-use var uses)))))
- (match cont
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) uses)
- (($ $values args)
- (add-uses args uses))
- (($ $call proc args)
- (add-uses args uses))
- (($ $branch kt ($ $values (arg)))
- (add-use arg uses))
- (($ $branch kt ($ $primcall name args))
- (add-uses args uses))
- (($ $primcall name args)
- (add-uses args uses))
- (($ $prompt escape? tag handler)
- (add-use tag uses))))
- (_ uses)))
- conts
- empty-intset)))
-
-(define (compute-singly-referenced-labels conts body)
- (define (add-ref label single multiple)
- (define (ref k single multiple)
- (if (intset-ref single k)
- (values single (intset-add! multiple k))
- (values (intset-add! single k) multiple)))
- (define (ref0) (values single multiple))
- (define (ref1 k) (ref k single multiple))
- (define (ref2 k k*)
- (if k*
- (let-values (((single multiple) (ref k single multiple)))
- (ref k* single multiple))
- (ref1 k)))
- (match (intmap-ref conts label)
- (($ $kreceive arity k) (ref1 k))
- (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
- (($ $ktail) (ref0))
- (($ $kclause arity kbody kalt) (ref2 kbody kalt))
- (($ $kargs names syms ($ $continue k src exp))
- (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
- (let*-values (((single multiple) (values empty-intset empty-intset))
- ((single multiple) (intset-fold add-ref body single multiple)))
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple))))
-
-(define (compute-function-names conts functions)
- "Compute a map of FUN-LABEL->BOUND-VAR... for each labelled function
-whose bound vars we know."
- (define (add-named-fun var kfun out)
- (let ((self (match (intmap-ref conts kfun)
- (($ $kfun src meta self) self))))
- (intmap-add out kfun (intset var self))))
- (intmap-fold
- (lambda (label body out)
- (let ((single (compute-singly-referenced-labels conts body)))
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue k _ ($ $fun kfun)))
- (if (intset-ref single k)
- (match (intmap-ref conts k)
- (($ $kargs (_) (var)) (add-named-fun var kfun out))
- (_ out))
- out))
- (($ $kargs _ _ ($ $continue k _ ($ $rec _ vars (($ $fun kfun) ...))))
- (unless (intset-ref single k)
- (error "$rec continuation has multiple predecessors??"))
- (fold add-named-fun out vars kfun))
- (_ out)))
- body
- out)))
- functions
- empty-intmap))
-
-(define (compute-well-known-functions conts bound->label)
- "Compute a set of labels indicating the well-known functions in
-@var{conts}. A well-known function is a function whose bound names we
-know and which is never used in a non-operator position."
- (intset-subtract
- (persistent-intset
- (intmap-fold (lambda (bound label candidates)
- (intset-add! candidates label))
- bound->label
- empty-intset))
- (persistent-intset
- (intset-fold (lambda (var not-well-known)
- (match (intmap-ref bound->label var (lambda (_) #f))
- (#f not-well-known)
- (label (intset-add! not-well-known label))))
- (compute-non-operator-uses conts)
- empty-intset))))
-
-(define (intset-cons i set)
- (intset-add set i))
-
-(define (compute-shared-closures conts well-known)
- "Compute a map LABEL->VAR indicating the sets of functions that will
-share a closure. If a functions's label is in the map, it is shared.
-The entries indicate the var of the shared closure, which will be one of
-the bound vars of the closure."
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs _ _
- ($ $continue _ _ ($ $rec names vars (($ $fun kfuns) ...))))
- ;; The split-rec pass should have ensured that this $rec forms a
- ;; strongly-connected component, so the free variables from all of
- ;; the functions will be alive as long as one of the closures is
- ;; alive. For that reason we can consider storing all free
- ;; variables in one closure and sharing it.
- (let* ((kfuns-set (fold intset-cons empty-intset kfuns))
- (unknown-kfuns (intset-subtract kfuns-set well-known)))
- (cond
- ((or (eq? empty-intset kfuns-set) (trivial-intset kfuns-set))
- ;; There is only zero or one function bound here. Trivially
- ;; shared already.
- out)
- ((eq? empty-intset unknown-kfuns)
- ;; All functions are well-known; we can share a closure. Use
- ;; the first bound variable.
- (let ((closure (car vars)))
- (intset-fold (lambda (kfun out)
- (intmap-add out kfun closure))
- kfuns-set out)))
- ((trivial-intset unknown-kfuns)
- => (lambda (unknown-kfun)
- ;; Only one function is not-well-known. Use that
- ;; function's closure as the shared closure.
- (let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
- (intset-fold (lambda (kfun out)
- (intmap-add out kfun closure))
- kfuns-set out))))
- (else
- ;; More than one not-well-known function means we need more
- ;; than one proper closure, so we can't share.
- out))))
- (_ out)))
- conts
- empty-intmap))
-
-(define* (rewrite-shared-closure-calls cps functions label->bound shared kfun)
- "Rewrite CPS such that every call to a function with a shared closure
-instead is a $callk to that label, but passing the shared closure as the
-proc argument. For recursive calls, use the appropriate 'self'
-variable, if possible. Also rewrite uses of the non-well-known but
-shared closures to use the appropriate 'self' variable, if possible."
- ;; env := var -> (var . label)
- (define (rewrite-fun kfun cps env)
- (define (subst var)
- (match (intmap-ref env var (lambda (_) #f))
- (#f var)
- ((var . label) var)))
-
- (define (rename-exp label cps names vars k src exp)
- (intmap-replace!
- cps label
- (build-cont
- ($kargs names vars
- ($continue k src
- ,(rewrite-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $call proc args)
- ,(let ((args (map subst args)))
- (rewrite-exp (intmap-ref env proc (lambda (_) #f))
- (#f ($call proc ,args))
- ((closure . label) ($callk label closure ,args)))))
- (($ $primcall name args)
- ($primcall name ,(map subst args)))
- (($ $branch k ($ $values (arg)))
- ($branch k ($values ((subst arg)))))
- (($ $branch k ($ $primcall name args))
- ($branch k ($primcall name ,(map subst args))))
- (($ $values args)
- ($values ,(map subst args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst tag) handler))))))))
-
- (define (visit-exp label cps names vars k src exp)
- (define (compute-env label bound self rec-bound rec-labels env)
- (define (add-bound-var bound label env)
- (intmap-add env bound (cons self label) (lambda (old new) new)))
- (if (intmap-ref shared label (lambda (_) #f))
- ;; Within a function with a shared closure, rewrite
- ;; references to bound vars to use the "self" var.
- (fold add-bound-var env rec-bound rec-labels)
- ;; Otherwise be sure to use "self" references in any
- ;; closure.
- (add-bound-var bound label env)))
- (match exp
- (($ $fun label)
- (rewrite-fun label cps env))
- (($ $rec names vars (($ $fun labels) ...))
- (fold (lambda (label var cps)
- (match (intmap-ref cps label)
- (($ $kfun src meta self)
- (rewrite-fun label cps
- (compute-env label var self vars labels
- env)))))
- cps labels vars))
- (_ (rename-exp label cps names vars k src exp))))
-
- (define (rewrite-cont label cps)
- (match (intmap-ref cps label)
- (($ $kargs names vars ($ $continue k src exp))
- (visit-exp label cps names vars k src exp))
- (_ cps)))
-
- (intset-fold rewrite-cont (intmap-ref functions kfun) cps))
-
- ;; Initial environment is bound-var -> (shared-var . label) map for
- ;; functions with shared closures.
- (let ((env (intmap-fold (lambda (label shared env)
- (intset-fold (lambda (bound env)
- (intmap-add env bound
- (cons shared label)))
- (intset-remove
- (intmap-ref label->bound label)
- (match (intmap-ref cps label)
- (($ $kfun src meta self) self)))
- env))
- shared
- empty-intmap)))
- (persistent-intmap (rewrite-fun kfun cps env))))
-
-(define (compute-free-vars conts kfun shared)
- "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
-references."
- (define (add-def var defs) (intset-add! defs var))
- (define (add-defs vars defs)
- (match vars
- (() defs)
- ((var . vars) (add-defs vars (add-def var defs)))))
- (define (add-use var uses)
- (intset-add! uses var))
- (define (add-uses vars uses)
- (match vars
- (() uses)
- ((var . vars) (add-uses vars (add-use var uses)))))
- (define (visit-nested-funs body)
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _
- ($ $fun kfun)))
- (intmap-union out (visit-fun kfun)))
- (($ $kargs _ _ ($ $continue _ _
- ($ $rec _ _ (($ $fun labels) ...))))
- (let* ((out (fold (lambda (kfun out)
- (intmap-union out (visit-fun kfun)))
- out labels))
- (free (fold (lambda (kfun free)
- (intset-union free (intmap-ref out kfun)))
- empty-intset labels)))
- (fold (lambda (kfun out)
- ;; For functions that share a closure, the free
- ;; variables for one will be the union of the free
- ;; variables for all.
- (if (intmap-ref shared kfun (lambda (_) #f))
- (intmap-replace out kfun free)
- out))
- out
- labels)))
- (_ out)))
- body
- empty-intmap))
- (define (visit-fun kfun)
- (let* ((body (compute-function-body conts kfun))
- (free (visit-nested-funs body)))
- (call-with-values
- (lambda ()
- (intset-fold
- (lambda (label defs uses)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (values
- (add-defs vars defs)
- (match exp
- ((or ($ $const) ($ $prim)) uses)
- (($ $fun kfun)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- (($ $rec names vars (($ $fun kfun) ...))
- (fold (lambda (kfun uses)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- uses kfun))
- (($ $values args)
- (add-uses args uses))
- (($ $call proc args)
- (add-use proc (add-uses args uses)))
- (($ $callk label proc args)
- (add-use proc (add-uses args uses)))
- (($ $branch kt ($ $values (arg)))
- (add-use arg uses))
- (($ $branch kt ($ $primcall name args))
- (add-uses args uses))
- (($ $primcall name args)
- (add-uses args uses))
- (($ $prompt escape? tag handler)
- (add-use tag uses)))))
- (($ $kfun src meta self)
- (values (add-def self defs) uses))
- (_ (values defs uses))))
- body empty-intset empty-intset))
- (lambda (defs uses)
- (intmap-add free kfun (intset-subtract
- (persistent-intset uses)
- (persistent-intset defs)))))))
- (visit-fun kfun))
-
-(define (eliminate-closure? label free-vars)
- (eq? (intmap-ref free-vars label) empty-intset))
-
-(define (closure-label label shared bound->label)
- (cond
- ((intmap-ref shared label (lambda (_) #f))
- => (lambda (closure)
- (intmap-ref bound->label closure)))
- (else label)))
-
-(define (closure-alias label well-known free-vars)
- (and (intset-ref well-known label)
- (trivial-intset (intmap-ref free-vars label))))
-
-(define (prune-free-vars free-vars bound->label well-known shared)
- "Given the label->bound-var map @var{free-vars}, remove free variables
-that are known functions with zero free variables, and replace
-references to well-known functions with one free variable with that free
-variable, until we reach a fixed point on the free-vars map."
- (define (prune-free in-label free free-vars)
- (intset-fold (lambda (var free)
- (match (intmap-ref bound->label var (lambda (_) #f))
- (#f free)
- (label
- (cond
- ((eliminate-closure? label free-vars)
- (intset-remove free var))
- ((closure-alias (closure-label label shared bound->label)
- well-known free-vars)
- => (lambda (alias)
- ;; If VAR is free in LABEL, then ALIAS must
- ;; also be free because its definition must
- ;; precede VAR's definition.
- (intset-add (intset-remove free var) alias)))
- (else free)))))
- free free))
- (fixpoint (lambda (free-vars)
- (intmap-fold (lambda (label free free-vars)
- (intmap-replace free-vars label
- (prune-free label free free-vars)))
- free-vars
- free-vars))
- free-vars))
-
-(define (intset-find set i)
- (let lp ((idx 0) (start #f))
- (let ((start (intset-next set start)))
- (cond
- ((not start) (error "not found" set i))
- ((= start i) idx)
- (else (lp (1+ idx) (1+ start)))))))
-
-(define (intset-count set)
- (intset-fold (lambda (_ count) (1+ count)) set 0))
-
-(define (convert-one cps label body free-vars bound->label well-known shared)
- (define (well-known? label)
- (intset-ref well-known label))
-
- (let* ((free (intmap-ref free-vars label))
- (nfree (intset-count free))
- (self-known? (well-known? (closure-label label shared bound->label)))
- (self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
- (define (convert-arg cps var k)
- "Convert one possibly free variable reference to a bound reference.
-
-If @var{var} is free, it is replaced by a closure reference via a
-@code{free-ref} primcall, and @var{k} is called with the new var.
-Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
- ;; We know that var is not the name of a well-known function.
- (cond
- ((and=> (intmap-ref bound->label var (lambda (_) #f))
- (lambda (kfun)
- (and (eq? empty-intset (intmap-ref free-vars kfun))
- kfun)))
- ;; A not-well-known function with zero free vars. Copy as a
- ;; constant, relying on the linker to reify just one copy.
- => (lambda (kfun)
- (with-cps cps
- (letv var*)
- (let$ body (k var*))
- (letk k* ($kargs (#f) (var*) ,body))
- (build-term ($continue k* #f ($closure kfun 0))))))
- ((intset-ref free var)
- (match (vector self-known? nfree)
- (#(#t 1)
- ;; A reference to the one free var of a well-known function.
- (with-cps cps
- ($ (k self))))
- (#(#t 2)
- ;; A reference to one of the two free vars in a well-known
- ;; function.
- (let ((op (if (= var (intset-next free)) 'car 'cdr)))
- (with-cps cps
- (letv var*)
- (let$ body (k var*))
- (letk k* ($kargs (#f) (var*) ,body))
- (build-term ($continue k* #f ($primcall op (self)))))))
- (_
- (let* ((idx (intset-find free var))
- (op (cond
- ((not self-known?) 'free-ref)
- ((<= idx #xff) 'vector-ref/immediate)
- (else 'vector-ref))))
- (with-cps cps
- (letv var*)
- (let$ body (k var*))
- (letk k* ($kargs (#f) (var*) ,body))
- ($ (with-cps-constants ((idx idx))
- (build-term
- ($continue k* #f ($primcall op (self idx)))))))))))
- (else
- (with-cps cps
- ($ (k var))))))
-
- (define (convert-args cps vars k)
- "Convert a number of possibly free references to bound references.
-@var{k} is called with the bound references, and should return the
-term."
- (match vars
- (()
- (with-cps cps
- ($ (k '()))))
- ((var . vars)
- (convert-arg cps var
- (lambda (cps var)
- (convert-args cps vars
- (lambda (cps vars)
- (with-cps cps
- ($ (k (cons var vars)))))))))))
-
- (define (allocate-closure cps k src label known? nfree)
- "Allocate a new closure, and pass it to $var{k}."
- (match (vector known? nfree)
- (#(#f nfree)
- ;; The call sites cannot be enumerated; allocate a closure.
- (with-cps cps
- (build-term ($continue k src ($closure label nfree)))))
- (#(#t 2)
- ;; Well-known closure with two free variables; the closure is a
- ;; pair.
- (with-cps cps
- ($ (with-cps-constants ((false #f))
- (build-term
- ($continue k src ($primcall 'cons (false false))))))))
- ;; Well-known callee with more than two free variables; the closure
- ;; is a vector.
- (#(#t nfree)
- (unless (> nfree 2)
- (error "unexpected well-known nullary, unary, or binary closure"))
- (let ((op (if (<= nfree #xff) 'make-vector/immediate 'make-vector)))
- (with-cps cps
- ($ (with-cps-constants ((nfree nfree)
- (false #f))
- (build-term
- ($continue k src ($primcall op (nfree false)))))))))))
-
- (define (init-closure cps k src var known? free)
- "Initialize the free variables @var{closure-free} in a closure
-bound to @var{var}, and continue to @var{k}."
- (match (vector known? (intset-count free))
- ;; Well-known callee with zero or one free variables; no
- ;; initialization necessary.
- (#(#t (or 0 1))
- (with-cps cps
- (build-term ($continue k src ($values ())))))
- ;; Well-known callee with two free variables; do a set-car! and
- ;; set-cdr!.
- (#(#t 2)
- (let* ((free0 (intset-next free))
- (free1 (intset-next free (1+ free0))))
- (convert-arg cps free0
- (lambda (cps v0)
- (with-cps cps
- (let$ body
- (convert-arg free1
- (lambda (cps v1)
- (with-cps cps
- (build-term
- ($continue k src
- ($primcall 'set-cdr! (var v1))))))))
- (letk kcdr ($kargs () () ,body))
- (build-term
- ($continue kcdr src ($primcall 'set-car! (var v0)))))))))
- ;; Otherwise residualize a sequence of vector-set! or free-set!,
- ;; depending on whether the callee is well-known or not.
- (_
- (let lp ((cps cps) (prev #f) (idx 0))
- (match (intset-next free prev)
- (#f (with-cps cps
- (build-term ($continue k src ($values ())))))
- (v (with-cps cps
- (let$ body (lp (1+ v) (1+ idx)))
- (letk k ($kargs () () ,body))
- ($ (convert-arg v
- (lambda (cps v)
- (with-cps cps
- ($ (with-cps-constants ((idx idx))
- (let ((op (cond
- ((not known?) 'free-set!)
- ((<= idx #xff) 'vector-set!/immediate)
- (else 'vector-set!))))
- (build-term
- ($continue k src
- ($primcall op (var idx v))))))))))))))))))
-
- (define (make-single-closure cps k src kfun)
- (let ((free (intmap-ref free-vars kfun)))
- (match (vector (well-known? kfun) (intset-count free))
- (#(#f 0)
- (with-cps cps
- (build-term ($continue k src ($closure kfun 0)))))
- (#(#t 0)
- (with-cps cps
- (build-term ($continue k src ($const #f)))))
- (#(#t 1)
- ;; A well-known closure of one free variable is replaced
- ;; at each use with the free variable itself, so we don't
- ;; need a binding at all; and yet, the continuation
- ;; expects one value, so give it something. DCE should
- ;; clean up later.
- (with-cps cps
- (build-term ($continue k src ($const #f)))))
- (#(well-known? nfree)
- ;; A bit of a mess, but beta conversion should remove the
- ;; final $values if possible.
- (with-cps cps
- (letv closure)
- (letk k* ($kargs () () ($continue k src ($values (closure)))))
- (let$ init (init-closure k* src closure well-known? free))
- (letk knew ($kargs (#f) (closure) ,init))
- ($ (allocate-closure knew src kfun well-known? nfree)))))))
-
- ;; The callee is known, but not necessarily well-known.
- (define (convert-known-proc-call cps k src label closure args)
- (define (have-closure cps closure)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($callk label closure args)))))))
- (cond
- ((eq? (intmap-ref free-vars label) empty-intset)
- ;; Known call, no free variables; no closure needed.
- ;; Pass #f as closure argument.
- (with-cps cps
- ($ (with-cps-constants ((false #f))
- ($ (have-closure false))))))
- ((and (well-known? (closure-label label shared bound->label))
- (trivial-intset (intmap-ref free-vars label)))
- ;; Well-known closures with one free variable are
- ;; replaced at their use sites by uses of the one free
- ;; variable.
- => (lambda (var)
- (convert-arg cps var have-closure)))
- (else
- ;; Otherwise just load the proc.
- (convert-arg cps closure have-closure))))
-
- (define (visit-term cps term)
- (match term
- (($ $continue k src (or ($ $const) ($ $prim)))
- (with-cps cps
- term))
-
- (($ $continue k src ($ $fun kfun))
- (with-cps cps
- ($ (make-single-closure k src kfun))))
-
- ;; Remove letrec.
- (($ $continue k src ($ $rec names vars (($ $fun kfuns) ...)))
- (match (vector names vars kfuns)
- (#(() () ())
- ;; Trivial empty case.
- (with-cps cps
- (build-term ($continue k src ($values ())))))
- (#((name) (var) (kfun))
- ;; Trivial single case. We have already proven that K has
- ;; only LABEL as its predecessor, so we have been able
- ;; already to rewrite free references to the bound name with
- ;; the self name.
- (with-cps cps
- ($ (make-single-closure k src kfun))))
- (#(_ _ (kfun0 . _))
- ;; A non-trivial strongly-connected component. Does it have
- ;; a shared closure?
- (match (intmap-ref shared kfun0 (lambda (_) #f))
- (#f
- ;; Nope. Allocate closures for each function.
- (let lp ((cps (match (intmap-ref cps k)
- ;; Steal declarations from the continuation.
- (($ $kargs names vals body)
- (intmap-replace cps k
- (build-cont
- ($kargs () () ,body))))))
- (in (map vector names vars kfuns))
- (init (lambda (cps)
- (with-cps cps
- (build-term
- ($continue k src ($values ())))))))
- (match in
- (() (init cps))
- ((#(name var kfun) . in)
- (let* ((known? (well-known? kfun))
- (free (intmap-ref free-vars kfun))
- (nfree (intset-count free)))
- (define (next-init cps)
- (with-cps cps
- (let$ body (init))
- (letk k ($kargs () () ,body))
- ($ (init-closure k src var known? free))))
- (with-cps cps
- (let$ body (lp in next-init))
- (letk k ($kargs (name) (var) ,body))
- ($ (allocate-closure k src kfun known? nfree))))))))
- (shared
- ;; If shared is in the bound->var map, that means one of
- ;; the functions is not well-known. Otherwise use kfun0
- ;; as the function label, but just so make-single-closure
- ;; can find the free vars, not for embedding in the
- ;; closure.
- (let* ((kfun (intmap-ref bound->label shared (lambda (_) kfun0)))
- (cps (match (intmap-ref cps k)
- ;; Make continuation declare only the shared
- ;; closure.
- (($ $kargs names vals body)
- (intmap-replace cps k
- (build-cont
- ($kargs (#f) (shared) ,body)))))))
- (with-cps cps
- ($ (make-single-closure k src kfun)))))))))
-
- (($ $continue k src ($ $call proc args))
- (match (intmap-ref bound->label proc (lambda (_) #f))
- (#f
- (convert-arg cps proc
- (lambda (cps proc)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($call proc args)))))))))
- (label
- (convert-known-proc-call cps k src label proc args))))
-
- (($ $continue k src ($ $callk label proc args))
- (convert-known-proc-call cps k src label proc args))
-
- (($ $continue k src ($ $primcall name args))
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($primcall name args)))))))
-
- (($ $continue k src ($ $branch kt ($ $primcall name args)))
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src
- ($branch kt ($primcall name args))))))))
-
- (($ $continue k src ($ $branch kt ($ $values (arg))))
- (convert-arg cps arg
- (lambda (cps arg)
- (with-cps cps
- (build-term
- ($continue k src
- ($branch kt ($values (arg)))))))))
-
- (($ $continue k src ($ $values args))
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($values args)))))))
-
- (($ $continue k src ($ $prompt escape? tag handler))
- (convert-arg cps tag
- (lambda (cps tag)
- (with-cps cps
- (build-term
- ($continue k src
- ($prompt escape? tag handler)))))))))
-
- (intset-fold (lambda (label cps)
- (match (intmap-ref cps label (lambda (_) #f))
- (($ $kargs names vars term)
- (with-cps cps
- (let$ term (visit-term term))
- (setk label ($kargs names vars ,term))))
- (_ cps)))
- body
- cps)))
-
-(define (convert-closures cps)
- "Convert free reference in @var{cps} to primcalls to @code{free-ref},
-and allocate and initialize flat closures."
- (let* ((kfun 0) ;; Ass-u-me.
- ;; label -> body-label...
- (functions (compute-function-bodies cps kfun))
- (cps (filter-reachable cps functions))
- ;; label -> bound-var...
- (label->bound (compute-function-names cps functions))
- ;; bound-var -> label
- (bound->label (invert-partition label->bound))
- ;; label...
- (well-known (compute-well-known-functions cps bound->label))
- ;; label -> closure-var
- (shared (compute-shared-closures cps well-known))
- (cps (rewrite-shared-closure-calls cps functions label->bound shared
- kfun))
- ;; label -> free-var...
- (free-vars (compute-free-vars cps kfun shared))
- (free-vars (prune-free-vars free-vars bound->label well-known shared)))
- (let ((free-in-program (intmap-ref free-vars kfun)))
- (unless (eq? empty-intset free-in-program)
- (error "Expected no free vars in program" free-in-program)))
- (with-fresh-name-state cps
- (persistent-intmap
- (intmap-fold
- (lambda (label body cps)
- (convert-one cps label body free-vars bound->label well-known shared))
- functions
- cps)))))
-
-;;; Local Variables:
-;;; eval: (put 'convert-arg 'scheme-indent-function 2)
-;;; eval: (put 'convert-args 'scheme-indent-function 2)
-;;; End:
diff --git a/module/language/cps2/compile-bytecode.scm b/module/language/cps2/compile-bytecode.scm
deleted file mode 100644
index a39c9f222..000000000
--- a/module/language/cps2/compile-bytecode.scm
+++ /dev/null
@@ -1,433 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Compiling CPS to bytecode. The result is in the bytecode language,
-;;; which happens to be an ELF image as a bytecode.
-;;;
-;;; Code:
-
-(define-module (language cps2 compile-bytecode)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (language cps2)
- #:use-module (language cps primitives)
- #:use-module (language cps2 slot-allocation)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 closure-conversion)
- #:use-module (language cps2 optimize)
- #:use-module (language cps2 reify-primitives)
- #:use-module (language cps2 renumber)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:use-module (system vm assembler)
- #:export (compile-bytecode))
-
-(define (kw-arg-ref args kw default)
- (match (memq kw args)
- ((_ val . _) val)
- (_ default)))
-
-(define (intmap-for-each f map)
- (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
-
-(define (intmap-select map set)
- (persistent-intmap
- (intset-fold
- (lambda (k out)
- (intmap-add! out k (intmap-ref map k)))
- set
- empty-intmap)))
-
-(define (compile-function cps asm)
- (let ((allocation (allocate-slots cps))
- (frame-size #f))
- (define (maybe-slot sym)
- (lookup-maybe-slot sym allocation))
-
- (define (slot sym)
- (lookup-slot sym allocation))
-
- (define (constant sym)
- (lookup-constant-value sym allocation))
-
- (define (maybe-mov dst src)
- (unless (= dst src)
- (emit-mov asm dst src)))
-
- (define (compile-tail label exp)
- ;; There are only three kinds of expressions in tail position:
- ;; tail calls, multiple-value returns, and single-value returns.
- (match exp
- (($ $call proc args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (emit-tail-call asm (1+ (length args))))
- (($ $callk k proc args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (emit-tail-call-label asm (1+ (length args)) k))
- (($ $values ())
- (emit-reset-frame asm 1)
- (emit-return-values asm))
- (($ $values (arg))
- (if (maybe-slot arg)
- (emit-return asm (slot arg))
- (begin
- (emit-load-constant asm 1 (constant arg))
- (emit-return asm 1))))
- (($ $values args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (emit-reset-frame asm (1+ (length args)))
- (emit-return-values asm))
- (($ $primcall 'return (arg))
- (emit-return asm (slot arg)))))
-
- (define (compile-value label exp dst)
- (match exp
- (($ $values (arg))
- (maybe-mov dst (slot arg)))
- (($ $const exp)
- (emit-load-constant asm dst exp))
- (($ $closure k 0)
- (emit-load-static-procedure asm dst k))
- (($ $closure k nfree)
- (emit-make-closure asm dst k nfree))
- (($ $primcall 'current-module)
- (emit-current-module asm dst))
- (($ $primcall 'cached-toplevel-box (scope name bound?))
- (emit-cached-toplevel-box asm dst (constant scope) (constant name)
- (constant bound?)))
- (($ $primcall 'cached-module-box (mod name public? bound?))
- (emit-cached-module-box asm dst (constant mod) (constant name)
- (constant public?) (constant bound?)))
- (($ $primcall 'resolve (name bound?))
- (emit-resolve asm dst (constant bound?) (slot name)))
- (($ $primcall 'free-ref (closure idx))
- (emit-free-ref asm dst (slot closure) (constant idx)))
- (($ $primcall 'vector-ref (vector index))
- (emit-vector-ref asm dst (slot vector) (slot index)))
- (($ $primcall 'make-vector (length init))
- (emit-make-vector asm dst (slot length) (slot init)))
- (($ $primcall 'make-vector/immediate (length init))
- (emit-make-vector/immediate asm dst (constant length) (slot init)))
- (($ $primcall 'vector-ref/immediate (vector index))
- (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
- (($ $primcall 'allocate-struct (vtable nfields))
- (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
- (($ $primcall 'allocate-struct/immediate (vtable nfields))
- (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
- (($ $primcall 'struct-ref (struct n))
- (emit-struct-ref asm dst (slot struct) (slot n)))
- (($ $primcall 'struct-ref/immediate (struct n))
- (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
- (($ $primcall 'builtin-ref (name))
- (emit-builtin-ref asm dst (constant name)))
- (($ $primcall 'bv-u8-ref (bv idx))
- (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s8-ref (bv idx))
- (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u16-ref (bv idx))
- (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s16-ref (bv idx))
- (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u32-ref (bv idx val))
- (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s32-ref (bv idx val))
- (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-u64-ref (bv idx val))
- (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-s64-ref (bv idx val))
- (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-f32-ref (bv idx val))
- (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
- (($ $primcall 'bv-f64-ref (bv idx val))
- (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
- (($ $primcall name args)
- ;; FIXME: Inline all the cases.
- (let ((inst (prim-instruction name)))
- (emit-text asm `((,inst ,dst ,@(map slot args))))))))
-
- (define (compile-effect label exp k)
- (match exp
- (($ $values ()) #f)
- (($ $prompt escape? tag handler)
- (match (intmap-ref cps handler)
- (($ $kreceive ($ $arity req () rest () #f) khandler-body)
- (let ((receive-args (gensym "handler"))
- (nreq (length req))
- (proc-slot (lookup-call-proc-slot label allocation)))
- (emit-prompt asm (slot tag) escape? proc-slot receive-args)
- (emit-br asm k)
- (emit-label asm receive-args)
- (unless (and rest (zero? nreq))
- (emit-receive-values asm proc-slot (->bool rest) nreq))
- (when (and rest
- (match (intmap-ref cps khandler-body)
- (($ $kargs names (_ ... rest))
- (maybe-slot rest))))
- (emit-bind-rest asm (+ proc-slot 1 nreq)))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves handler allocation))
- (emit-reset-frame asm frame-size)
- (emit-br asm khandler-body)))))
- (($ $primcall 'cache-current-module! (sym scope))
- (emit-cache-current-module! asm (slot sym) (constant scope)))
- (($ $primcall 'free-set! (closure idx value))
- (emit-free-set! asm (slot closure) (slot value) (constant idx)))
- (($ $primcall 'box-set! (box value))
- (emit-box-set! asm (slot box) (slot value)))
- (($ $primcall 'struct-set! (struct index value))
- (emit-struct-set! asm (slot struct) (slot index) (slot value)))
- (($ $primcall 'struct-set!/immediate (struct index value))
- (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
- (($ $primcall 'vector-set! (vector index value))
- (emit-vector-set! asm (slot vector) (slot index) (slot value)))
- (($ $primcall 'vector-set!/immediate (vector index value))
- (emit-vector-set!/immediate asm (slot vector) (constant index)
- (slot value)))
- (($ $primcall 'set-car! (pair value))
- (emit-set-car! asm (slot pair) (slot value)))
- (($ $primcall 'set-cdr! (pair value))
- (emit-set-cdr! asm (slot pair) (slot value)))
- (($ $primcall 'define! (sym value))
- (emit-define! asm (slot sym) (slot value)))
- (($ $primcall 'push-fluid (fluid val))
- (emit-push-fluid asm (slot fluid) (slot val)))
- (($ $primcall 'pop-fluid ())
- (emit-pop-fluid asm))
- (($ $primcall 'wind (winder unwinder))
- (emit-wind asm (slot winder) (slot unwinder)))
- (($ $primcall 'bv-u8-set! (bv idx val))
- (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s8-set! (bv idx val))
- (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u16-set! (bv idx val))
- (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s16-set! (bv idx val))
- (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u32-set! (bv idx val))
- (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s32-set! (bv idx val))
- (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-u64-set! (bv idx val))
- (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-s64-set! (bv idx val))
- (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-f32-set! (bv idx val))
- (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'bv-f64-set! (bv idx val))
- (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
- (($ $primcall 'unwind ())
- (emit-unwind asm))))
-
- (define (compile-values label exp syms)
- (match exp
- (($ $values args)
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation)))))
-
- (define (compile-test label exp kt kf next-label)
- (define (unary op sym)
- (cond
- ((eq? kt next-label)
- (op asm (slot sym) #t kf))
- (else
- (op asm (slot sym) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (define (binary op a b)
- (cond
- ((eq? kt next-label)
- (op asm (slot a) (slot b) #t kf))
- (else
- (op asm (slot a) (slot b) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (match exp
- (($ $values (sym))
- (call-with-values (lambda ()
- (lookup-maybe-constant-value sym allocation))
- (lambda (has-const? val)
- (if has-const?
- (if val
- (unless (eq? kt next-label)
- (emit-br asm kt))
- (unless (eq? kf next-label)
- (emit-br asm kf)))
- (unary emit-br-if-true sym)))))
- (($ $primcall 'null? (a)) (unary emit-br-if-null a))
- (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
- (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
- (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
- (($ $primcall 'char? (a)) (unary emit-br-if-char a))
- (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
- (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
- (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
- (($ $primcall 'string? (a)) (unary emit-br-if-string a))
- (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
- (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
- (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
- ;; Add more TC7 tests here. Keep in sync with
- ;; *branching-primcall-arities* in (language cps primitives) and
- ;; the set of macro-instructions in assembly.scm.
- (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
- (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
- (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
- (($ $primcall '< (a b)) (binary emit-br-if-< a b))
- (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
- (($ $primcall '= (a b)) (binary emit-br-if-= a b))
- (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
- (($ $primcall '> (a b)) (binary emit-br-if-< b a))
- (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
-
- (define (compile-trunc label k exp nreq rest-var)
- (define (do-call proc args emit-call)
- (let* ((proc-slot (lookup-call-proc-slot label allocation))
- (nargs (1+ (length args)))
- (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves label allocation))
- (emit-call asm proc-slot nargs)
- (emit-dead-slot-map asm proc-slot
- (lookup-dead-slot-map label allocation))
- (cond
- ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
- (match (lookup-parallel-moves k allocation)
- ((((? (lambda (src) (= src (1+ proc-slot))) src)
- . dst)) dst)
- (_ #f)))
- ;; The usual case: one required live return value, ignoring
- ;; any additional values.
- => (lambda (dst)
- (emit-receive asm dst proc-slot frame-size)))
- (else
- (unless (and (zero? nreq) rest-var)
- (emit-receive-values asm proc-slot (->bool rest-var) nreq))
- (when (and rest-var (maybe-slot rest-var))
- (emit-bind-rest asm (+ proc-slot 1 nreq)))
- (for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
- (lookup-parallel-moves k allocation))
- (emit-reset-frame asm frame-size)))))
- (match exp
- (($ $call proc args)
- (do-call proc args
- (lambda (asm proc-slot nargs)
- (emit-call asm proc-slot nargs))))
- (($ $callk k proc args)
- (do-call proc args
- (lambda (asm proc-slot nargs)
- (emit-call-label asm proc-slot nargs k))))))
-
- (define (compile-expression label k exp)
- (let* ((fallthrough? (= k (1+ label))))
- (define (maybe-emit-jump)
- (unless fallthrough?
- (emit-br asm k)))
- (match (intmap-ref cps k)
- (($ $ktail)
- (compile-tail label exp))
- (($ $kargs (name) (sym))
- (let ((dst (maybe-slot sym)))
- (when dst
- (compile-value label exp dst)))
- (maybe-emit-jump))
- (($ $kargs () ())
- (match exp
- (($ $branch kt exp)
- (compile-test label exp kt k (1+ label)))
- (_
- (compile-effect label exp k)
- (maybe-emit-jump))))
- (($ $kargs names syms)
- (compile-values label exp syms)
- (maybe-emit-jump))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (compile-trunc label k exp (length req)
- (and rest
- (match (intmap-ref cps kargs)
- (($ $kargs names (_ ... rest)) rest))))
- (unless (and fallthrough? (= kargs (1+ k)))
- (emit-br asm kargs))))))
-
- (define (compile-cont label cont)
- (match cont
- (($ $kfun src meta self tail clause)
- (when src
- (emit-source asm src))
- (emit-begin-program asm label meta))
- (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alt)
- (let ((first? (match (intmap-ref cps (1- label))
- (($ $kfun) #t)
- (_ #f)))
- (kw-indices (map (match-lambda
- ((key name sym)
- (cons key (lookup-slot sym allocation))))
- kw)))
- (unless first?
- (emit-end-arity asm))
- (emit-label asm label)
- (set! frame-size (lookup-nlocals label allocation))
- (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
- frame-size alt)))
- (($ $kargs names vars ($ $continue k src exp))
- (emit-label asm label)
- (for-each (lambda (name var)
- (let ((slot (maybe-slot var)))
- (when slot
- (emit-definition asm name slot))))
- names vars)
- (when src
- (emit-source asm src))
- (compile-expression label k exp))
- (($ $kreceive arity kargs)
- (emit-label asm label))
- (($ $ktail)
- (emit-end-arity asm)
- (emit-end-program asm))))
-
- (intmap-for-each compile-cont cps)))
-
-(define (emit-bytecode exp env opts)
- (let ((asm (make-assembler)))
- (intmap-for-each (lambda (kfun body)
- (compile-function (intmap-select exp body) asm))
- (compute-reachable-functions exp 0))
- (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
- env
- env)))
-
-(define (lower-cps exp opts)
- (set! exp (optimize-higher-order-cps exp opts))
- (set! exp (convert-closures exp))
- (set! exp (optimize-first-order-cps exp opts))
- (set! exp (reify-primitives exp))
- (renumber exp))
-
-(define (compile-bytecode exp env opts)
- (set! exp (lower-cps exp opts))
- (emit-bytecode exp env opts))
diff --git a/module/language/cps2/constructors.scm b/module/language/cps2/constructors.scm
deleted file mode 100644
index e4973f2b7..000000000
--- a/module/language/cps2/constructors.scm
+++ /dev/null
@@ -1,98 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Constructor inlining turns "list" primcalls into a series of conses,
-;;; and does similar transformations for "vector".
-;;;
-;;; Code:
-
-(define-module (language cps2 constructors)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:export (inline-constructors))
-
-(define (inline-list out k src args)
- (define (build-list out args k)
- (match args
- (()
- (with-cps out
- (build-term ($continue k src ($const '())))))
- ((arg . args)
- (with-cps out
- (letv tail)
- (letk ktail ($kargs ('tail) (tail)
- ($continue k src
- ($primcall 'cons (arg tail)))))
- ($ (build-list args ktail))))))
- (with-cps out
- (letv val)
- (letk kvalues ($kargs ('val) (val)
- ($continue k src
- ($primcall 'values (val)))))
- ($ (build-list args kvalues))))
-
-(define (inline-vector out k src args)
- (define (initialize out vec args n)
- (match args
- (()
- (with-cps out
- (build-term ($continue k src ($primcall 'values (vec))))))
- ((arg . args)
- (with-cps out
- (let$ next (initialize vec args (1+ n)))
- (letk knext ($kargs () () ,next))
- ($ (with-cps-constants ((idx n))
- (build-term ($continue knext src
- ($primcall 'vector-set! (vec idx arg))))))))))
- (with-cps out
- (letv vec)
- (let$ body (initialize vec args 0))
- (letk kalloc ($kargs ('vec) (vec) ,body))
- ($ (with-cps-constants ((len (length args))
- (init #f))
- (build-term ($continue kalloc src
- ($primcall 'make-vector (len init))))))))
-
-(define (find-constructor-inliner name)
- (match name
- ('list inline-list)
- ('vector inline-vector)
- (_ #f)))
-
-(define (inline-constructors conts)
- (with-fresh-name-state conts
- (persistent-intmap
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
- (let ((inline (find-constructor-inliner name)))
- (if inline
- (call-with-values (lambda () (inline out k src args))
- (lambda (out term)
- (intmap-replace! out label
- (build-cont ($kargs names vars ,term)))))
- out)))
- (_ out)))
- conts
- conts))))
diff --git a/module/language/cps2/contification.scm b/module/language/cps2/contification.scm
deleted file mode 100644
index e15544af2..000000000
--- a/module/language/cps2/contification.scm
+++ /dev/null
@@ -1,475 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Contification is a pass that turns $fun instances into $cont
-;;; instances if all calls to the $fun return to the same continuation.
-;;; This is a more rigorous variant of our old "fixpoint labels
-;;; allocation" optimization.
-;;;
-;;; See Kennedy's "Compiling with Continuations, Continued", and Fluet
-;;; and Weeks's "Contification using Dominators".
-;;;
-;;; Code:
-
-(define-module (language cps2 contification)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-11)
- #:use-module ((srfi srfi-1) #:select (fold))
- #:use-module (language cps2)
- #:use-module (language cps2 renumber)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (contify))
-
-(define (compute-singly-referenced-labels conts)
- "Compute the set of labels in CONTS that have exactly one
-predecessor."
- (define (add-ref label cont single multiple)
- (define (ref k single multiple)
- (if (intset-ref single k)
- (values single (intset-add! multiple k))
- (values (intset-add! single k) multiple)))
- (define (ref0) (values single multiple))
- (define (ref1 k) (ref k single multiple))
- (define (ref2 k k*)
- (if k*
- (let-values (((single multiple) (ref k single multiple)))
- (ref k* single multiple))
- (ref1 k)))
- (match cont
- (($ $kreceive arity k) (ref1 k))
- (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
- (($ $ktail) (ref0))
- (($ $kclause arity kbody kalt) (ref2 kbody kalt))
- (($ $kargs names syms ($ $continue k src exp))
- (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
- (let*-values (((single multiple) (values empty-intset empty-intset))
- ((single multiple) (intmap-fold add-ref conts single multiple)))
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple))))
-
-(define (compute-functions conts)
- "Compute a map from $kfun label to bound variable names for all
-functions in CONTS. Functions have two bound variable names: their self
-binding, and the name they are given in their continuation. If their
-continuation has more than one predecessor, then the bound variable name
-doesn't uniquely identify the function, so we exclude that function from
-the set."
- (define (function-self label)
- (match (intmap-ref conts label)
- (($ $kfun src meta self) self)))
- (let ((single (compute-singly-referenced-labels conts)))
- (intmap-fold (lambda (label cont functions)
- (match cont
- (($ $kargs _ _ ($ $continue k src ($ $fun kfun)))
- (if (intset-ref single k)
- (match (intmap-ref conts k)
- (($ $kargs (name) (var))
- (intmap-add functions kfun
- (intset var (function-self kfun)))))
- functions))
- (($ $kargs _ _ ($ $continue k src
- ($ $rec _ vars (($ $fun kfuns) ...))))
- (if (intset-ref single k)
- (fold (lambda (var kfun functions)
- (intmap-add functions kfun
- (intset var (function-self kfun))))
- functions vars kfuns)
- functions))
- (_ functions)))
- conts
- empty-intmap)))
-
-(define (compute-multi-clause conts)
- "Compute an set containing all labels that are part of a multi-clause
-case-lambda. See the note in compute-contification-candidates."
- (define (multi-clause? clause)
- (and clause
- (match (intmap-ref conts clause)
- (($ $kclause arity body alt)
- alt))))
- (intmap-fold (lambda (label cont multi)
- (match cont
- (($ $kfun src meta self tail clause)
- (if (multi-clause? clause)
- (intset-union multi (compute-function-body conts label))
- multi))
- (_ multi)))
- conts
- empty-intset))
-
-(define (compute-arities conts functions)
- "Given the map FUNCTIONS whose keys are $kfun labels, return a map
-from label to arities."
- (define (clause-arities clause)
- (if clause
- (match (intmap-ref conts clause)
- (($ $kclause arity body alt)
- (cons arity (clause-arities alt))))
- '()))
- (intmap-map (lambda (label vars)
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail clause)
- (clause-arities clause))))
- functions))
-
-;; For now, we don't contify functions with optional, keyword, or rest
-;; arguments.
-(define (contifiable-arity? arity)
- (match arity
- (($ $arity req () #f () aok?)
- #t)
- (_
- #f)))
-
-(define (arity-matches? arity nargs)
- (match arity
- (($ $arity req () #f () aok?)
- (= nargs (length req)))
- (_
- #f)))
-
-(define (compute-contification-candidates conts)
- "Compute and return a label -> (variable ...) map describing all
-functions with known uses that are only ever used as the operator of a
-$call, and are always called with a compatible arity."
- (let* ((functions (compute-functions conts))
- (multi-clause (compute-multi-clause conts))
- (vars (intmap-fold (lambda (label vars out)
- (intset-fold (lambda (var out)
- (intmap-add out var label))
- vars out))
- functions
- empty-intmap))
- (arities (compute-arities conts functions)))
- (define (restrict-arity functions proc nargs)
- (match (intmap-ref vars proc (lambda (_) #f))
- (#f functions)
- (label
- (let lp ((arities (intmap-ref arities label)))
- (match arities
- (() (intmap-remove functions label))
- ((arity . arities)
- (cond
- ((not (contifiable-arity? arity)) (lp '()))
- ((arity-matches? arity nargs) functions)
- (else (lp arities)))))))))
- (define (visit-cont label cont functions)
- (define (exclude-var functions var)
- (match (intmap-ref vars var (lambda (_) #f))
- (#f functions)
- (label (intmap-remove functions label))))
- (define (exclude-vars functions vars)
- (match vars
- (() functions)
- ((var . vars)
- (exclude-vars (exclude-var functions var) vars))))
- (match cont
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- ((or ($ $const) ($ $prim) ($ $closure) ($ $fun) ($ $rec))
- functions)
- (($ $values args)
- (exclude-vars functions args))
- (($ $call proc args)
- (let ((functions (exclude-vars functions args)))
- ;; This contification algorithm is happy to contify the
- ;; `lp' in this example into a shared tail between clauses:
- ;;
- ;; (letrec ((lp (lambda () (lp))))
- ;; (case-lambda
- ;; ((a) (lp))
- ;; ((a b) (lp))))
- ;;
- ;; However because the current compilation pipeline has to
- ;; re-nest continuations into old CPS, there would be no
- ;; scope in which the tail would be valid. So, until the
- ;; old compilation pipeline is completely replaced,
- ;; conservatively exclude contifiable fucntions called
- ;; from multi-clause procedures.
- (if (intset-ref multi-clause label)
- (exclude-var functions proc)
- (restrict-arity functions proc (length args)))))
- (($ $callk k proc args)
- (exclude-vars functions (cons proc args)))
- (($ $branch kt ($ $primcall name args))
- (exclude-vars functions args))
- (($ $branch kt ($ $values (arg)))
- (exclude-var functions arg))
- (($ $primcall name args)
- (exclude-vars functions args))
- (($ $prompt escape? tag handler)
- (exclude-var functions tag))))
- (_ functions)))
- (intmap-fold visit-cont conts functions)))
-
-(define (compute-call-graph conts labels vars)
- "Given the set of contifiable functions LABELS and associated bound
-variables VARS, compute and return two values: a map
-LABEL->LABEL... indicating the contifiable functions called by a
-function, and a map LABEL->LABEL... indicating the return continuations
-for a function. The first return value also has an entry
-0->LABEL... indicating all contifiable functions called by
-non-contifiable functions. We assume that 0 is not in the contifiable
-function set."
- (let ((bodies
- ;; label -> fun-label for all labels in bodies of contifiable
- ;; functions
- (intset-fold (lambda (fun-label bodies)
- (intset-fold (lambda (label bodies)
- (intmap-add bodies label fun-label))
- (compute-function-body conts fun-label)
- bodies))
- labels
- empty-intmap)))
- (when (intset-ref labels 0)
- (error "internal error: label 0 should not be contifiable"))
- (intmap-fold
- (lambda (label cont calls returns)
- (match cont
- (($ $kargs _ _ ($ $continue k src ($ $call proc)))
- (match (intmap-ref vars proc (lambda (_) #f))
- (#f (values calls returns))
- (callee
- (let ((caller (intmap-ref bodies label (lambda (_) 0))))
- (values (intmap-add calls caller callee intset-add)
- (intmap-add returns callee k intset-add))))))
- (_ (values calls returns))))
- conts
- (intset->intmap (lambda (label) empty-intset) (intset-add labels 0))
- (intset->intmap (lambda (label) empty-intset) labels))))
-
-(define (tail-label conts label)
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail body)
- tail)))
-
-(define (compute-return-labels labels tails returns return-substs)
- (define (subst k)
- (match (intmap-ref return-substs k (lambda (_) #f))
- (#f k)
- (k (subst k))))
- ;; Compute all return labels, then subtract tail labels of the
- ;; functions in question.
- (intset-subtract
- ;; Return labels for all calls to these labels.
- (intset-fold (lambda (label out)
- (intset-fold (lambda (k out)
- (intset-add out (subst k)))
- (intmap-ref returns label)
- out))
- labels
- empty-intset)
- (intset-fold (lambda (label out)
- (intset-add out (intmap-ref tails label)))
- labels
- empty-intset)))
-
-(define (intmap->intset map)
- (define (add-key label cont labels)
- (intset-add labels label))
- (intmap-fold add-key map empty-intset))
-
-(define (filter-contifiable contified groups)
- (intmap-fold (lambda (id labels groups)
- (let ((labels (intset-subtract labels contified)))
- (if (eq? empty-intset labels)
- groups
- (intmap-add groups id labels))))
- groups
- empty-intmap))
-
-(define (trivial-set set)
- (let ((first (intset-next set)))
- (and first
- (not (intset-next set (1+ first)))
- first)))
-
-(define (compute-contification conts)
- (let*-values
- (;; label -> (var ...)
- ((candidates) (compute-contification-candidates conts))
- ((labels) (intmap->intset candidates))
- ;; var -> label
- ((vars) (intmap-fold (lambda (label vars out)
- (intset-fold (lambda (var out)
- (intmap-add out var label))
- vars out))
- candidates
- empty-intmap))
- ;; caller-label -> callee-label..., callee-label -> return-label...
- ((calls returns) (compute-call-graph conts labels vars))
- ;; callee-label -> tail-label
- ((tails) (intset-fold
- (lambda (label tails)
- (intmap-add tails label (tail-label conts label)))
- labels
- empty-intmap))
- ;; Strongly connected components, allowing us to contify mutually
- ;; tail-recursive functions. Since `compute-call-graph' added on
- ;; a synthetic 0->LABEL... entry for contifiable functions called
- ;; by non-contifiable functions, we need to remove that entry
- ;; from the partition. It will be in its own component, as it
- ;; has no predecessors.
- ;;
- ;; id -> label...
- ((groups) (intmap-remove
- (compute-strongly-connected-components calls 0)
- 0)))
- ;; todo: thread groups through contification
- (define (attempt-contification labels contified return-substs)
- (let ((returns (compute-return-labels labels tails returns
- return-substs)))
- (cond
- ((trivial-set returns)
- => (lambda (k)
- ;; Success!
- (values (intset-union contified labels)
- (intset-fold (lambda (label return-substs)
- (let ((tail (intmap-ref tails label)))
- (intmap-add return-substs tail k)))
- labels return-substs))))
- ((trivial-set labels)
- ;; Single-label SCC failed to contify.
- (values contified return-substs))
- (else
- ;; Multi-label SCC failed to contify. Try instead to contify
- ;; each one.
- (intset-fold
- (lambda (label contified return-substs)
- (let ((labels (intset-add empty-intset label)))
- (attempt-contification labels contified return-substs)))
- labels contified return-substs)))))
- (call-with-values
- (lambda ()
- (fixpoint
- (lambda (contified return-substs)
- (intmap-fold
- (lambda (id group contified return-substs)
- (attempt-contification group contified return-substs))
- (filter-contifiable contified groups)
- contified
- return-substs))
- empty-intset
- empty-intmap))
- (lambda (contified return-substs)
- (values (intset-fold (lambda (label call-substs)
- (intset-fold
- (lambda (var call-substs)
- (intmap-add call-substs var label))
- (intmap-ref candidates label)
- call-substs))
- contified
- empty-intmap)
- return-substs)))))
-
-(define (apply-contification conts call-substs return-substs)
- (define (call-subst proc)
- (intmap-ref call-substs proc (lambda (_) #f)))
- (define (return-subst k)
- (intmap-ref return-substs k (lambda (_) #f)))
- (define (find-body kfun nargs)
- (match (intmap-ref conts kfun)
- (($ $kfun src meta self tail clause)
- (let lp ((clause clause))
- (match (intmap-ref conts clause)
- (($ $kclause arity body alt)
- (if (arity-matches? arity nargs)
- body
- (lp alt))))))))
- (define (continue k src exp)
- (define (lookup-return-cont k)
- (match (return-subst k)
- (#f k)
- (k (lookup-return-cont k))))
- (let ((k* (lookup-return-cont k)))
- (if (eq? k k*)
- (build-term ($continue k src ,exp))
- ;; We are contifying this return. It must be a call, a
- ;; $values expression, or a return primcall. k* will be
- ;; either a $ktail or a $kreceive continuation. CPS2 has this
- ;; thing though where $kreceive can't be the target of a
- ;; $values expression, and "return" can only continue to a
- ;; tail continuation, so we might have to rewrite to a
- ;; "values" primcall.
- (build-term
- ($continue k* src
- ,(match (intmap-ref conts k*)
- (($ $kreceive)
- (match exp
- (($ $primcall 'return (val))
- (build-exp ($primcall 'values (val))))
- (($ $call) exp)
- ;; Except for 'return, a primcall that can continue
- ;; to $ktail can also continue to $kreceive. TODO:
- ;; replace 'return with 'values, for consistency.
- (($ $primcall) exp)
- (($ $values vals)
- (build-exp ($primcall 'values vals)))))
- (($ $ktail) exp)))))))
- (define (visit-exp k src exp)
- (match exp
- (($ $call proc args)
- ;; If proc is contifiable, replace call with jump.
- (match (call-subst proc)
- (#f (continue k src exp))
- (kfun
- (let ((body (find-body kfun (length args))))
- (build-term ($continue body src ($values args)))))))
- (($ $fun kfun)
- ;; If the function's tail continuation has been
- ;; substituted, that means it has been contified.
- (if (return-subst (tail-label conts kfun))
- (continue k src (build-exp ($values ())))
- (continue k src exp)))
- (($ $rec names vars funs)
- (match (filter (match-lambda ((n v f) (not (call-subst v))))
- (map list names vars funs))
- (() (continue k src (build-exp ($values ()))))
- (((names vars funs) ...)
- (continue k src (build-exp ($rec names vars funs))))))
- (_ (continue k src exp))))
-
- ;; Renumbering is not strictly necessary but some passes may not be
- ;; equipped to deal with stale $kfun nodes whose bodies have been
- ;; wired into other functions.
- (renumber
- (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- ;; Remove bindings for functions that have been contified.
- (match (filter (match-lambda ((name var) (not (call-subst var))))
- (map list names vars))
- (((names vars) ...)
- (build-cont
- ($kargs names vars ,(visit-exp k src exp))))))
- (_ cont)))
- conts)))
-
-(define (contify conts)
- ;; FIXME: Renumbering isn't really needed but dead continuations may
- ;; cause compute-singly-referenced-labels to spuriously mark some
- ;; conts as irreducible. For now we punt and renumber so that there
- ;; are only live conts.
- (let ((conts (renumber conts)))
- (let-values (((call-substs return-substs) (compute-contification conts)))
- (apply-contification conts call-substs return-substs))))
diff --git a/module/language/cps2/cse.scm b/module/language/cps2/cse.scm
deleted file mode 100644
index b5ac14d31..000000000
--- a/module/language/cps2/cse.scm
+++ /dev/null
@@ -1,449 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Common subexpression elimination for CPS.
-;;;
-;;; Code:
-
-(define-module (language cps2 cse)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 effects-analysis)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (eliminate-common-subexpressions))
-
-(define (intset-pop set)
- (match (intset-next set)
- (#f (values set #f))
- (i (values (intset-remove set i) i))))
-
-(define-syntax-rule (make-worklist-folder* seed ...)
- (lambda (f worklist seed ...)
- (let lp ((worklist worklist) (seed seed) ...)
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist i)
- (if i
- (call-with-values (lambda () (f i seed ...))
- (lambda (i* seed ...)
- (let add ((i* i*) (worklist worklist))
- (match i*
- (() (lp worklist seed ...))
- ((i . i*) (add i* (intset-add worklist i)))))))
- (values seed ...)))))))
-
-(define worklist-fold*
- (case-lambda
- ((f worklist seed)
- ((make-worklist-folder* seed) f worklist seed))))
-
-(define (compute-available-expressions conts kfun effects)
- "Compute and return a map of LABEL->ANCESTOR..., where ANCESTOR... is
-an intset containing ancestor labels whose value is available at LABEL."
- (define (propagate avail succ out)
- (let* ((in (intmap-ref avail succ (lambda (_) #f)))
- (in* (if in (intset-intersect in out) out)))
- (if (eq? in in*)
- (values '() avail)
- (values (list succ)
- (intmap-add avail succ in* (lambda (old new) new))))))
-
- (define (clobber label in)
- (let ((fx (intmap-ref effects label)))
- (cond
- ((not (causes-effect? fx &write))
- ;; Fast-path if this expression clobbers nothing.
- in)
- (else
- ;; Kill clobbered expressions. FIXME: there is no need to check
- ;; on any label before than the last dominating label that
- ;; clobbered everything. Another way to speed things up would
- ;; be to compute a clobber set per-effect, which we could
- ;; subtract from "in".
- (let lp ((label 0) (in in))
- (cond
- ((intset-next in label)
- => (lambda (label)
- (if (effect-clobbers? fx (intmap-ref effects label))
- (lp (1+ label) (intset-remove in label))
- (lp (1+ label) in))))
- (else in)))))))
-
- (define (visit-cont label avail)
- (let* ((in (intmap-ref avail label))
- (out (intset-add (clobber label in) label)))
- (define (propagate0)
- (values '() avail))
- (define (propagate1 succ)
- (propagate avail succ out))
- (define (propagate2 succ0 succ1)
- (let*-values (((changed0 avail) (propagate avail succ0 out))
- ((changed1 avail) (propagate avail succ1 out)))
- (values (append changed0 changed1) avail)))
-
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (match exp
- (($ $branch kt) (propagate2 k kt))
- (($ $prompt escape? tag handler) (propagate2 k handler))
- (_ (propagate1 k))))
- (($ $kreceive arity k)
- (propagate1 k))
- (($ $kfun src meta self tail clause)
- (if clause
- (propagate1 clause)
- (propagate0)))
- (($ $kclause arity kbody kalt)
- (if kalt
- (propagate2 kbody kalt)
- (propagate1 kbody)))
- (($ $ktail) (propagate0)))))
-
- (worklist-fold* visit-cont
- (intset kfun)
- (intmap-add empty-intmap kfun empty-intset)))
-
-(define (compute-truthy-expressions conts kfun boolv)
- "Compute a \"truth map\", indicating which expressions can be shown to
-be true and/or false at each label in the function starting at KFUN..
-Returns an intmap of intsets. The even elements of the intset indicate
-labels that may be true, and the odd ones indicate those that may be
-false. It could be that both true and false proofs are available."
- (define (true-idx label) (ash label 1))
- (define (false-idx label) (1+ (ash label 1)))
-
- (define (propagate boolv succ out)
- (let* ((in (intmap-ref boolv succ (lambda (_) #f)))
- (in* (if in (intset-intersect in out) out)))
- (if (eq? in in*)
- (values '() boolv)
- (values (list succ)
- (intmap-add boolv succ in* (lambda (old new) new))))))
-
- (define (visit-cont label boolv)
- (let ((in (intmap-ref boolv label)))
- (define (propagate0)
- (values '() boolv))
- (define (propagate1 succ)
- (propagate boolv succ in))
- (define (propagate2 succ0 succ1)
- (let*-values (((changed0 boolv) (propagate boolv succ0 in))
- ((changed1 boolv) (propagate boolv succ1 in)))
- (values (append changed0 changed1) boolv)))
- (define (propagate-branch succ0 succ1)
- (let*-values (((changed0 boolv)
- (propagate boolv succ0
- (intset-add in (false-idx label))))
- ((changed1 boolv)
- (propagate boolv succ1
- (intset-add in (true-idx label)))))
- (values (append changed0 changed1) boolv)))
-
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (match exp
- (($ $branch kt) (propagate-branch k kt))
- (($ $prompt escape? tag handler) (propagate2 k handler))
- (_ (propagate1 k))))
- (($ $kreceive arity k)
- (propagate1 k))
- (($ $kfun src meta self tail clause)
- (if clause
- (propagate1 clause)
- (propagate0)))
- (($ $kclause arity kbody kalt)
- (if kalt
- (propagate2 kbody kalt)
- (propagate1 kbody)))
- (($ $ktail) (propagate0)))))
-
- (let ((boolv (worklist-fold* visit-cont
- (intset kfun)
- (intmap-add boolv kfun empty-intset))))
- ;; Now visit nested functions. We don't do this in the worklist
- ;; folder because that would be exponential.
- (define (recurse kfun boolv)
- (compute-truthy-expressions conts kfun boolv))
- (intset-fold
- (lambda (label boolv)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- (($ $fun kfun) (recurse kfun boolv))
- (($ $rec _ _ (($ $fun kfun) ...)) (fold recurse boolv kfun))
- (_ boolv)))
- (_ boolv)))
- (compute-function-body conts kfun)
- boolv)))
-
-(define (intset-map f set)
- (persistent-intmap
- (intset-fold (lambda (i out) (intmap-add! out i (f i)))
- set
- empty-intmap)))
-
-;; Returns a map of label-idx -> (var-idx ...) indicating the variables
-;; defined by a given labelled expression.
-(define (compute-defs conts kfun)
- (intset-map (lambda (label)
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail clause)
- (list self))
- (($ $kclause arity body alt)
- (match (intmap-ref conts body)
- (($ $kargs names vars) vars)))
- (($ $kreceive arity kargs)
- (match (intmap-ref conts kargs)
- (($ $kargs names vars) vars)))
- (($ $ktail)
- '())
- (($ $kargs names vars ($ $continue k))
- (match (intmap-ref conts k)
- (($ $kargs names vars) vars)
- (_ #f)))))
- (compute-function-body conts kfun)))
-
-(define (compute-singly-referenced succs)
- (define (visit label succs single multiple)
- (intset-fold (lambda (label single multiple)
- (if (intset-ref single label)
- (values single (intset-add! multiple label))
- (values (intset-add! single label) multiple)))
- succs single multiple))
- (call-with-values (lambda ()
- (intmap-fold visit succs empty-intset empty-intset))
- (lambda (single multiple)
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple)))))
-
-(define (compute-equivalent-subexpressions conts kfun effects
- equiv-labels var-substs)
- (let* ((succs (compute-successors conts kfun))
- (singly-referenced (compute-singly-referenced succs))
- (avail (compute-available-expressions conts kfun effects))
- (defs (compute-defs conts kfun))
- (equiv-set (make-hash-table)))
- (define (subst-var var-substs var)
- (intmap-ref var-substs var (lambda (var) var)))
- (define (subst-vars var-substs vars)
- (let lp ((vars vars))
- (match vars
- (() '())
- ((var . vars) (cons (subst-var var-substs var) (lp vars))))))
-
- (define (compute-exp-key var-substs exp)
- (match exp
- (($ $const val) (cons 'const val))
- (($ $prim name) (cons 'prim name))
- (($ $fun body) #f)
- (($ $rec names syms funs) #f)
- (($ $call proc args) #f)
- (($ $callk k proc args) #f)
- (($ $primcall name args)
- (cons* 'primcall name (subst-vars var-substs args)))
- (($ $branch _ ($ $primcall name args))
- (cons* 'primcall name (subst-vars var-substs args)))
- (($ $branch) #f)
- (($ $values args) #f)
- (($ $prompt escape? tag handler) #f)))
-
- (define (add-auxiliary-definitions! label var-substs exp-key)
- (define (subst var)
- (subst-var var-substs var))
- (let ((defs (intmap-ref defs label)))
- (define (add-def! aux-key var)
- (let ((equiv (hash-ref equiv-set aux-key '())))
- (hash-set! equiv-set aux-key
- (acons label (list var) equiv))))
- (match exp-key
- (('primcall 'box val)
- (match defs
- ((box)
- (add-def! `(primcall box-ref ,(subst box)) val))))
- (('primcall 'box-set! box val)
- (add-def! `(primcall box-ref ,box) val))
- (('primcall 'cons car cdr)
- (match defs
- ((pair)
- (add-def! `(primcall car ,(subst pair)) car)
- (add-def! `(primcall cdr ,(subst pair)) cdr))))
- (('primcall 'set-car! pair car)
- (add-def! `(primcall car ,pair) car))
- (('primcall 'set-cdr! pair cdr)
- (add-def! `(primcall cdr ,pair) cdr))
- (('primcall (or 'make-vector 'make-vector/immediate) len fill)
- (match defs
- ((vec)
- (add-def! `(primcall vector-length ,(subst vec)) len))))
- (('primcall 'vector-set! vec idx val)
- (add-def! `(primcall vector-ref ,vec ,idx) val))
- (('primcall 'vector-set!/immediate vec idx val)
- (add-def! `(primcall vector-ref/immediate ,vec ,idx) val))
- (('primcall (or 'allocate-struct 'allocate-struct/immediate)
- vtable size)
- (match defs
- ((struct)
- (add-def! `(primcall struct-vtable ,(subst struct))
- vtable))))
- (('primcall 'struct-set! struct n val)
- (add-def! `(primcall struct-ref ,struct ,n) val))
- (('primcall 'struct-set!/immediate struct n val)
- (add-def! `(primcall struct-ref/immediate ,struct ,n) val))
- (_ #t))))
-
- (define (visit-label label equiv-labels var-substs)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (let* ((exp-key (compute-exp-key var-substs exp))
- (equiv (hash-ref equiv-set exp-key '()))
- (fx (intmap-ref effects label))
- (avail (intmap-ref avail label)))
- (define (finish equiv-labels var-substs)
- (define (recurse kfun equiv-labels var-substs)
- (compute-equivalent-subexpressions conts kfun effects
- equiv-labels var-substs))
- ;; If this expression defines auxiliary definitions,
- ;; as `cons' does for the results of `car' and `cdr',
- ;; define those. Do so after finding equivalent
- ;; expressions, so that we can take advantage of
- ;; subst'd output vars.
- (add-auxiliary-definitions! label var-substs exp-key)
- (match exp
- ;; If we see a $fun, recurse to add to the result.
- (($ $fun kfun)
- (recurse kfun equiv-labels var-substs))
- (($ $rec names vars (($ $fun kfun) ...))
- (fold2 recurse kfun equiv-labels var-substs))
- (_
- (values equiv-labels var-substs))))
- (let lp ((candidates equiv))
- (match candidates
- (()
- ;; No matching expressions. Add our expression
- ;; to the equivalence set, if appropriate. Note
- ;; that expressions that allocate a fresh object
- ;; or change the current fluid environment can't
- ;; be eliminated by CSE (though DCE might do it
- ;; if the value proves to be unused, in the
- ;; allocation case).
- (when (and exp-key
- (not (causes-effect? fx &allocation))
- (not (effect-clobbers? fx (&read-object &fluid))))
- (let ((defs (and (intset-ref singly-referenced k)
- (intmap-ref defs label))))
- (when defs
- (hash-set! equiv-set exp-key
- (acons label defs equiv)))))
- (finish equiv-labels var-substs))
- (((and head (candidate . vars)) . candidates)
- (cond
- ((not (intset-ref avail candidate))
- ;; This expression isn't available here; try
- ;; the next one.
- (lp candidates))
- (else
- ;; Yay, a match. Mark expression as equivalent. If
- ;; we provide the definitions for the successor, mark
- ;; the vars for substitution.
- (finish (intmap-add equiv-labels label head)
- (let ((defs (and (intset-ref singly-referenced k)
- (intmap-ref defs label))))
- (if defs
- (fold (lambda (def var var-substs)
- (intmap-add var-substs def var))
- var-substs defs vars)
- var-substs))))))))))
- (_ (values equiv-labels var-substs))))
-
- ;; Traverse the labels in fun in reverse post-order, which will
- ;; visit definitions before uses first.
- (fold2 visit-label
- (compute-reverse-post-order succs kfun)
- equiv-labels
- var-substs)))
-
-(define (apply-cse conts equiv-labels var-substs truthy-labels)
- (define (true-idx idx) (ash idx 1))
- (define (false-idx idx) (1+ (ash idx 1)))
-
- (define (subst-var var)
- (intmap-ref var-substs var (lambda (var) var)))
-
- (define (visit-exp exp)
- (rewrite-exp exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) ,exp)
- (($ $call proc args)
- ($call (subst-var proc) ,(map subst-var args)))
- (($ $callk k proc args)
- ($callk k (subst-var proc) ,(map subst-var args)))
- (($ $primcall name args)
- ($primcall name ,(map subst-var args)))
- (($ $branch k exp)
- ($branch k ,(visit-exp exp)))
- (($ $values args)
- ($values ,(map subst-var args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst-var tag) handler))))
-
- (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- (build-cont
- ($kargs names vars
- ,(match (intmap-ref equiv-labels label (lambda (_) #f))
- ((equiv . vars)
- (match exp
- (($ $branch kt exp)
- (let* ((bool (intmap-ref truthy-labels label))
- (t (intset-ref bool (true-idx equiv)))
- (f (intset-ref bool (false-idx equiv))))
- (if (eqv? t f)
- (build-term
- ($continue k src
- ($branch kt ,(visit-exp exp))))
- (build-term
- ($continue (if t kt k) src ($values ()))))))
- (_
- ;; For better or for worse, we only replace primcalls
- ;; if they have an associated VM op, which allows
- ;; them to continue to $kargs and thus we know their
- ;; defs and can use a $values expression instead of a
- ;; values primcall.
- (build-term
- ($continue k src ($values vars))))))
- (#f
- (build-term
- ($continue k src ,(visit-exp exp))))))))
- (_ cont)))
- conts))
-
-(define (eliminate-common-subexpressions conts)
- (call-with-values
- (lambda ()
- (let ((effects (synthesize-definition-effects (compute-effects conts))))
- (compute-equivalent-subexpressions conts 0 effects
- empty-intmap empty-intmap)))
- (lambda (equiv-labels var-substs)
- (let ((truthy-labels (compute-truthy-expressions conts 0 empty-intmap)))
- (apply-cse conts equiv-labels var-substs truthy-labels)))))
diff --git a/module/language/cps2/dce.scm b/module/language/cps2/dce.scm
deleted file mode 100644
index e743bc4a6..000000000
--- a/module/language/cps2/dce.scm
+++ /dev/null
@@ -1,399 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; This pass kills dead expressions: code that has no side effects, and
-;;; whose value is unused. It does so by marking all live values, and
-;;; then discarding other values as dead. This happens recursively
-;;; through procedures, so it should be possible to elide dead
-;;; procedures as well.
-;;;
-;;; Code:
-
-(define-module (language cps2 dce)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (language cps2)
- #:use-module (language cps2 effects-analysis)
- #:use-module (language cps2 renumber)
- #:use-module (language cps2 types)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (eliminate-dead-code))
-
-(define (elide-type-checks conts kfun effects)
- "Elide &type-check effects from EFFECTS for the function starting at
-KFUN where we can prove that no assertion will be raised at run-time."
- (let ((types (infer-types conts kfun)))
- (define (visit-primcall effects fx label name args)
- (if (primcall-types-check? types label name args)
- (intmap-replace! effects label (logand fx (lognot &type-check)))
- effects))
- (persistent-intmap
- (intmap-fold (lambda (label types effects)
- (let ((fx (intmap-ref effects label)))
- (cond
- ((causes-all-effects? fx) effects)
- ((causes-effect? fx &type-check)
- (match (intmap-ref conts label)
- (($ $kargs _ _ exp)
- (match exp
- (($ $continue k src ($ $primcall name args))
- (visit-primcall effects fx label name args))
- (($ $continue k src
- ($ $branch _ ($primcall name args)))
- (visit-primcall effects fx label name args))
- (_ effects)))
- (_ effects)))
- (else effects))))
- types
- effects))))
-
-(define (compute-effects/elide-type-checks conts)
- (intmap-fold (lambda (label cont effects)
- (match cont
- (($ $kfun) (elide-type-checks conts label effects))
- (_ effects)))
- conts
- (compute-effects conts)))
-
-(define (fold-local-conts proc conts label seed)
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail clause)
- (let lp ((label label) (seed seed))
- (if (<= label tail)
- (lp (1+ label) (proc label (intmap-ref conts label) seed))
- seed)))))
-
-(define (postorder-fold-local-conts2 proc conts label seed0 seed1)
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail clause)
- (let ((start label))
- (let lp ((label tail) (seed0 seed0) (seed1 seed1))
- (if (<= start label)
- (let ((cont (intmap-ref conts label)))
- (call-with-values (lambda () (proc label cont seed0 seed1))
- (lambda (seed0 seed1)
- (lp (1- label) seed0 seed1))))
- (values seed0 seed1)))))))
-
-(define (compute-known-allocations conts effects)
- "Compute the variables bound in CONTS that have known allocation
-sites."
- ;; Compute the set of conts that are called with freshly allocated
- ;; values, and subtract from that set the conts that might be called
- ;; with values with unknown allocation sites. Then convert that set
- ;; of conts into a set of bound variables.
- (call-with-values
- (lambda ()
- (intmap-fold (lambda (label cont known unknown)
- ;; Note that we only need to add labels to the
- ;; known/unknown sets if the labels can bind
- ;; values. So there's no need to add tail,
- ;; clause, branch alternate, or prompt handler
- ;; labels, as they bind no values.
- (match cont
- (($ $kargs _ _ ($ $continue k))
- (let ((fx (intmap-ref effects label)))
- (if (and (not (causes-all-effects? fx))
- (causes-effect? fx &allocation))
- (values (intset-add! known k) unknown)
- (values known (intset-add! unknown k)))))
- (($ $kreceive arity kargs)
- (values known (intset-add! unknown kargs)))
- (($ $kfun src meta self tail clause)
- (values known unknown))
- (($ $kclause arity body alt)
- (values known (intset-add! unknown body)))
- (($ $ktail)
- (values known unknown))))
- conts
- empty-intset
- empty-intset))
- (lambda (known unknown)
- (persistent-intset
- (intset-fold (lambda (label vars)
- (match (intmap-ref conts label)
- (($ $kargs (_) (var)) (intset-add! vars var))
- (_ vars)))
- (intset-subtract (persistent-intset known)
- (persistent-intset unknown))
- empty-intset)))))
-
-(define (compute-live-code conts)
- (let* ((effects (compute-effects/elide-type-checks conts))
- (known-allocations (compute-known-allocations conts effects)))
- (define (adjoin-var var set)
- (intset-add set var))
- (define (adjoin-vars vars set)
- (match vars
- (() set)
- ((var . vars) (adjoin-vars vars (adjoin-var var set)))))
- (define (var-live? var live-vars)
- (intset-ref live-vars var))
- (define (any-var-live? vars live-vars)
- (match vars
- (() #f)
- ((var . vars)
- (or (var-live? var live-vars)
- (any-var-live? vars live-vars)))))
- (define (cont-defs k)
- (match (intmap-ref conts k)
- (($ $kargs _ vars) vars)
- (_ #f)))
-
- (define (visit-live-exp label k exp live-labels live-vars)
- (match exp
- ((or ($ $const) ($ $prim))
- (values live-labels live-vars))
- (($ $fun body)
- (values (intset-add live-labels body) live-vars))
- (($ $closure body)
- (values (intset-add live-labels body) live-vars))
- (($ $rec names vars (($ $fun kfuns) ...))
- (let lp ((vars vars) (kfuns kfuns)
- (live-labels live-labels) (live-vars live-vars))
- (match (vector vars kfuns)
- (#(() ()) (values live-labels live-vars))
- (#((var . vars) (kfun . kfuns))
- (lp vars kfuns
- (if (var-live? var live-vars)
- (intset-add live-labels kfun)
- live-labels)
- live-vars)))))
- (($ $prompt escape? tag handler)
- (values live-labels (adjoin-var tag live-vars)))
- (($ $call proc args)
- (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
- (($ $callk kfun proc args)
- (values (intset-add live-labels kfun)
- (adjoin-vars args (adjoin-var proc live-vars))))
- (($ $primcall name args)
- (values live-labels (adjoin-vars args live-vars)))
- (($ $branch k ($ $primcall name args))
- (values live-labels (adjoin-vars args live-vars)))
- (($ $branch k ($ $values (arg)))
- (values live-labels (adjoin-var arg live-vars)))
- (($ $values args)
- (values live-labels
- (match (cont-defs k)
- (#f (adjoin-vars args live-vars))
- (defs (fold (lambda (use def live-vars)
- (if (var-live? def live-vars)
- (adjoin-var use live-vars)
- live-vars))
- live-vars args defs)))))))
-
- (define (visit-exp label k exp live-labels live-vars)
- (cond
- ((intset-ref live-labels label)
- ;; Expression live already.
- (visit-live-exp label k exp live-labels live-vars))
- ((let ((defs (cont-defs k))
- (fx (intmap-ref effects label)))
- (or
- ;; No defs; perhaps continuation is $ktail.
- (not defs)
- ;; We don't remove branches.
- (match exp (($ $branch) #t) (_ #f))
- ;; Do we have a live def?
- (any-var-live? defs live-vars)
- ;; Does this expression cause all effects? If so, it's
- ;; definitely live.
- (causes-all-effects? fx)
- ;; Does it cause a type check, but we weren't able to prove
- ;; that the types check?
- (causes-effect? fx &type-check)
- ;; We might have a setter. If the object being assigned to
- ;; is live or was not created by us, then this expression is
- ;; live. Otherwise the value is still dead.
- (and (causes-effect? fx &write)
- (match exp
- (($ $primcall
- (or 'vector-set! 'vector-set!/immediate
- 'set-car! 'set-cdr!
- 'box-set!)
- (obj . _))
- (or (var-live? obj live-vars)
- (not (intset-ref known-allocations obj))))
- (_ #t)))))
- ;; Mark expression as live and visit.
- (visit-live-exp label k exp (intset-add live-labels label) live-vars))
- (else
- ;; Still dead.
- (values live-labels live-vars))))
-
- (define (visit-fun label live-labels live-vars)
- ;; Visit uses before definitions.
- (postorder-fold-local-conts2
- (lambda (label cont live-labels live-vars)
- (match cont
- (($ $kargs _ _ ($ $continue k src exp))
- (visit-exp label k exp live-labels live-vars))
- (($ $kreceive arity kargs)
- (values live-labels live-vars))
- (($ $kclause arity kargs kalt)
- (values live-labels (adjoin-vars (cont-defs kargs) live-vars)))
- (($ $kfun src meta self)
- (values live-labels (adjoin-var self live-vars)))
- (($ $ktail)
- (values live-labels live-vars))))
- conts label live-labels live-vars))
-
- (fixpoint (lambda (live-labels live-vars)
- (let lp ((label 0)
- (live-labels live-labels)
- (live-vars live-vars))
- (match (intset-next live-labels label)
- (#f (values live-labels live-vars))
- (label
- (call-with-values
- (lambda ()
- (match (intmap-ref conts label)
- (($ $kfun)
- (visit-fun label live-labels live-vars))
- (_ (values live-labels live-vars))))
- (lambda (live-labels live-vars)
- (lp (1+ label) live-labels live-vars)))))))
- (intset 0)
- empty-intset)))
-
-(define-syntax adjoin-conts
- (syntax-rules ()
- ((_ (exp ...) clause ...)
- (let ((cps (exp ...)))
- (adjoin-conts cps clause ...)))
- ((_ cps (label cont) clause ...)
- (adjoin-conts (intmap-add! cps label (build-cont cont))
- clause ...))
- ((_ cps)
- cps)))
-
-(define (process-eliminations conts live-labels live-vars)
- (define (label-live? label)
- (intset-ref live-labels label))
- (define (value-live? var)
- (intset-ref live-vars var))
- (define (make-adaptor k src defs)
- (let* ((names (map (lambda (_) 'tmp) defs))
- (vars (map (lambda (_) (fresh-var)) defs))
- (live (filter-map (lambda (def var)
- (and (value-live? def) var))
- defs vars)))
- (build-cont
- ($kargs names vars
- ($continue k src ($values live))))))
- (define (visit-term label term cps)
- (match term
- (($ $continue k src exp)
- (if (label-live? label)
- (match exp
- (($ $fun body)
- (values cps
- term))
- (($ $closure body nfree)
- (values cps
- term))
- (($ $rec names vars funs)
- (match (filter-map (lambda (name var fun)
- (and (value-live? var)
- (list name var fun)))
- names vars funs)
- (()
- (values cps
- (build-term ($continue k src ($values ())))))
- (((names vars funs) ...)
- (values cps
- (build-term ($continue k src
- ($rec names vars funs)))))))
- (_
- (match (intmap-ref conts k)
- (($ $kargs ())
- (values cps term))
- (($ $kargs names ((? value-live?) ...))
- (values cps term))
- (($ $kargs names vars)
- (match exp
- (($ $values args)
- (let ((args (filter-map (lambda (use def)
- (and (value-live? def) use))
- args vars)))
- (values cps
- (build-term
- ($continue k src ($values args))))))
- (_
- (let-fresh (adapt) ()
- (values (adjoin-conts cps
- (adapt ,(make-adaptor k src vars)))
- (build-term
- ($continue adapt src ,exp)))))))
- (_
- (values cps term)))))
- (values cps
- (build-term
- ($continue k src ($values ()))))))))
- (define (visit-cont label cont cps)
- (match cont
- (($ $kargs names vars term)
- (match (filter-map (lambda (name var)
- (and (value-live? var)
- (cons name var)))
- names vars)
- (((names . vars) ...)
- (call-with-values (lambda () (visit-term label term cps))
- (lambda (cps term)
- (adjoin-conts cps
- (label ($kargs names vars ,term))))))))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (let ((defs (match (intmap-ref conts kargs)
- (($ $kargs names vars) vars))))
- (if (and-map value-live? defs)
- (adjoin-conts cps (label ,cont))
- (let-fresh (adapt) ()
- (adjoin-conts cps
- (adapt ,(make-adaptor kargs #f defs))
- (label ($kreceive req rest adapt)))))))
- (_
- (adjoin-conts cps (label ,cont)))))
- (with-fresh-name-state conts
- (persistent-intmap
- (intmap-fold (lambda (label cont cps)
- (match cont
- (($ $kfun)
- (if (label-live? label)
- (fold-local-conts visit-cont conts label cps)
- cps))
- (_ cps)))
- conts
- empty-intmap))))
-
-(define (eliminate-dead-code conts)
- ;; We work on a renumbered program so that we can easily visit uses
- ;; before definitions just by visiting higher-numbered labels before
- ;; lower-numbered labels. Renumbering is also a precondition for type
- ;; inference.
- (let ((conts (renumber conts)))
- (call-with-values (lambda () (compute-live-code conts))
- (lambda (live-labels live-vars)
- (process-eliminations conts live-labels live-vars)))))
-
-;;; Local Variables:
-;;; eval: (put 'adjoin-conts 'scheme-indent-function 1)
-;;; End:
diff --git a/module/language/cps2/effects-analysis.scm b/module/language/cps2/effects-analysis.scm
deleted file mode 100644
index ef5d8c8e9..000000000
--- a/module/language/cps2/effects-analysis.scm
+++ /dev/null
@@ -1,484 +0,0 @@
-;;; Effects analysis on CPS
-
-;; Copyright (C) 2011, 2012, 2013, 2014, 2015 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:
-;;;
-;;; A helper module to compute the set of effects caused by an
-;;; expression. This information is useful when writing algorithms that
-;;; move code around, while preserving the semantics of an input
-;;; program.
-;;;
-;;; The effects set is represented as an integer with three parts. The
-;;; low 4 bits indicate effects caused by an expression, as a bitfield.
-;;; The next 4 bits indicate the kind of memory accessed by the
-;;; expression, if it accesses mutable memory. Finally the rest of the
-;;; bits indicate the field in the object being accessed, if known, or
-;;; -1 for unknown.
-;;;
-;;; In this way we embed a coarse type-based alias analysis in the
-;;; effects analysis. For example, a "car" call is modelled as causing
-;;; a read to field 0 on a &pair, and causing a &type-check effect. If
-;;; any intervening code sets the car of any pair, that will block
-;;; motion of the "car" call, because any write to field 0 of a pair is
-;;; seen by effects analysis as being a write to field 0 of all pairs.
-;;;
-;;; Code:
-
-(define-module (language cps2 effects-analysis)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (ice-9 match)
- #:export (expression-effects
- compute-effects
- synthesize-definition-effects
-
- &allocation
- &type-check
- &read
- &write
-
- &fluid
- &prompt
- &car
- &cdr
- &vector
- &box
- &module
- &struct
- &string
- &bytevector
-
- &object
- &field
-
- &allocate
- &read-object
- &read-field
- &write-object
- &write-field
-
- &no-effects
- &all-effects
-
- exclude-effects
- effect-free?
- constant?
- causes-effect?
- causes-all-effects?
- effect-clobbers?))
-
-(define-syntax define-flags
- (lambda (x)
- (syntax-case x ()
- ((_ all shift name ...)
- (let ((count (length #'(name ...))))
- (with-syntax (((n ...) (iota count))
- (count count))
- #'(begin
- (define-syntax name (identifier-syntax (ash 1 n)))
- ...
- (define-syntax all (identifier-syntax (1- (ash 1 count))))
- (define-syntax shift (identifier-syntax count)))))))))
-
-(define-syntax define-enumeration
- (lambda (x)
- (define (count-bits n)
- (let lp ((out 1))
- (if (< n (ash 1 (1- out)))
- out
- (lp (1+ out)))))
- (syntax-case x ()
- ((_ mask shift name ...)
- (let* ((len (length #'(name ...)))
- (bits (count-bits len)))
- (with-syntax (((n ...) (iota len))
- (bits bits))
- #'(begin
- (define-syntax name (identifier-syntax n))
- ...
- (define-syntax mask (identifier-syntax (1- (ash 1 bits))))
- (define-syntax shift (identifier-syntax bits)))))))))
-
-(define-flags &all-effect-kinds &effect-kind-bits
- ;; Indicates that an expression may cause a type check. A type check,
- ;; for the purposes of this analysis, is the possibility of throwing
- ;; an exception the first time an expression is evaluated. If the
- ;; expression did not cause an exception to be thrown, users can
- ;; assume that evaluating the expression again will not cause an
- ;; exception to be thrown.
- ;;
- ;; For example, (+ x y) might throw if X or Y are not numbers. But if
- ;; it doesn't throw, it should be safe to elide a dominated, common
- ;; subexpression (+ x y).
- &type-check
-
- ;; Indicates that an expression may return a fresh object. The kind
- ;; of object is indicated in the object kind field.
- &allocation
-
- ;; Indicates that an expression may cause a read from memory. The
- ;; kind of memory is given in the object kind field. Some object
- ;; kinds have finer-grained fields; those are expressed in the "field"
- ;; part of the effects value. -1 indicates "the whole object".
- &read
-
- ;; Indicates that an expression may cause a write to memory.
- &write)
-
-(define-enumeration &memory-kind-mask &memory-kind-bits
- ;; Indicates than an expression may access unknown kinds of memory.
- &unknown-memory-kinds
-
- ;; Indicates that an expression depends on the value of a fluid
- ;; variable, or on the current fluid environment.
- &fluid
-
- ;; Indicates that an expression depends on the current prompt
- ;; stack.
- &prompt
-
- ;; Indicates that an expression depends on the value of the car or cdr
- ;; of a pair.
- &pair
-
- ;; Indicates that an expression depends on the value of a vector
- ;; field. The effect field indicates the specific field, or zero for
- ;; an unknown field.
- &vector
-
- ;; Indicates that an expression depends on the value of a variable
- ;; cell.
- &box
-
- ;; Indicates that an expression depends on the current module.
- &module
-
- ;; Indicates that an expression depends on the value of a struct
- ;; field. The effect field indicates the specific field, or zero for
- ;; an unknown field.
- &struct
-
- ;; Indicates that an expression depends on the contents of a string.
- &string
-
- ;; Indicates that an expression depends on the contents of a
- ;; bytevector. We cannot be more precise, as bytevectors may alias
- ;; other bytevectors.
- &bytevector)
-
-(define-inlinable (&field kind field)
- (ash (logior (ash field &memory-kind-bits) kind) &effect-kind-bits))
-(define-inlinable (&object kind)
- (&field kind -1))
-
-(define-inlinable (&allocate kind)
- (logior &allocation (&object kind)))
-(define-inlinable (&read-field kind field)
- (logior &read (&field kind field)))
-(define-inlinable (&read-object kind)
- (logior &read (&object kind)))
-(define-inlinable (&write-field kind field)
- (logior &write (&field kind field)))
-(define-inlinable (&write-object kind)
- (logior &write (&object kind)))
-
-(define-syntax &no-effects (identifier-syntax 0))
-(define-syntax &all-effects
- (identifier-syntax
- (logior &all-effect-kinds (&object &unknown-memory-kinds))))
-
-(define-inlinable (constant? effects)
- (zero? effects))
-
-(define-inlinable (causes-effect? x effects)
- (not (zero? (logand x effects))))
-
-(define-inlinable (causes-all-effects? x)
- (eqv? x &all-effects))
-
-(define (effect-clobbers? a b)
- "Return true if A clobbers B. This is the case if A is a write, and B
-is or might be a read or a write to the same location as A."
- (define (locations-same?)
- (let ((a (ash a (- &effect-kind-bits)))
- (b (ash b (- &effect-kind-bits))))
- (or (eqv? &unknown-memory-kinds (logand a &memory-kind-mask))
- (eqv? &unknown-memory-kinds (logand b &memory-kind-mask))
- (and (eqv? (logand a &memory-kind-mask) (logand b &memory-kind-mask))
- ;; A negative field indicates "the whole object".
- ;; Non-negative fields indicate only part of the object.
- (or (< a 0) (< b 0) (= a b))))))
- (and (not (zero? (logand a &write)))
- (not (zero? (logand b (logior &read &write))))
- (locations-same?)))
-
-(define-inlinable (indexed-field kind var constants)
- (let ((val (intmap-ref constants var (lambda (_) #f))))
- (if (and (exact-integer? val) (<= 0 val))
- (&field kind val)
- (&object kind))))
-
-(define *primitive-effects* (make-hash-table))
-
-(define-syntax-rule (define-primitive-effects* constants
- ((name . args) effects ...)
- ...)
- (begin
- (hashq-set! *primitive-effects* 'name
- (case-lambda*
- ((constants . args) (logior effects ...))
- (_ &all-effects)))
- ...))
-
-(define-syntax-rule (define-primitive-effects ((name . args) effects ...) ...)
- (define-primitive-effects* constants ((name . args) effects ...) ...))
-
-;; Miscellaneous.
-(define-primitive-effects
- ((values . _)))
-
-;; Generic effect-free predicates.
-(define-primitive-effects
- ((eq? . _))
- ((eqv? . _))
- ((equal? . _))
- ((pair? arg))
- ((null? arg))
- ((nil? arg ))
- ((symbol? arg))
- ((variable? arg))
- ((vector? arg))
- ((struct? arg))
- ((string? arg))
- ((number? arg))
- ((char? arg))
- ((bytevector? arg))
- ((keyword? arg))
- ((bitvector? arg))
- ((procedure? arg))
- ((thunk? arg)))
-
-;; Fluids.
-(define-primitive-effects
- ((fluid-ref f) (&read-object &fluid) &type-check)
- ((fluid-set! f v) (&write-object &fluid) &type-check)
- ((push-fluid f v) (&write-object &fluid) &type-check)
- ((pop-fluid) (&write-object &fluid) &type-check))
-
-;; Prompts.
-(define-primitive-effects
- ((make-prompt-tag #:optional arg) (&allocate &unknown-memory-kinds)))
-
-;; Pairs.
-(define-primitive-effects
- ((cons a b) (&allocate &pair))
- ((list . _) (&allocate &pair))
- ((car x) (&read-field &pair 0) &type-check)
- ((set-car! x y) (&write-field &pair 0) &type-check)
- ((cdr x) (&read-field &pair 1) &type-check)
- ((set-cdr! x y) (&write-field &pair 1) &type-check)
- ((memq x y) (&read-object &pair) &type-check)
- ((memv x y) (&read-object &pair) &type-check)
- ((list? arg) (&read-field &pair 1))
- ((length l) (&read-field &pair 1) &type-check))
-
-;; Variables.
-(define-primitive-effects
- ((box v) (&allocate &box))
- ((box-ref v) (&read-object &box) &type-check)
- ((box-set! v x) (&write-object &box) &type-check))
-
-;; Vectors.
-(define (vector-field n constants)
- (indexed-field &vector n constants))
-(define (read-vector-field n constants)
- (logior &read (vector-field n constants)))
-(define (write-vector-field n constants)
- (logior &write (vector-field n constants)))
-(define-primitive-effects* constants
- ((vector . _) (&allocate &vector))
- ((make-vector n init) (&allocate &vector) &type-check)
- ((make-vector/immediate n init) (&allocate &vector))
- ((vector-ref v n) (read-vector-field n constants) &type-check)
- ((vector-ref/immediate v n) (read-vector-field n constants) &type-check)
- ((vector-set! v n x) (write-vector-field n constants) &type-check)
- ((vector-set!/immediate v n x) (write-vector-field n constants) &type-check)
- ((vector-length v) &type-check))
-
-;; Structs.
-(define (struct-field n constants)
- (indexed-field &struct n constants))
-(define (read-struct-field n constants)
- (logior &read (struct-field n constants)))
-(define (write-struct-field n constants)
- (logior &write (struct-field n constants)))
-(define-primitive-effects* constants
- ((allocate-struct vt n) (&allocate &struct) &type-check)
- ((allocate-struct/immediate v n) (&allocate &struct) &type-check)
- ((make-struct vt ntail . _) (&allocate &struct) &type-check)
- ((make-struct/no-tail vt . _) (&allocate &struct) &type-check)
- ((struct-ref s n) (read-struct-field n constants) &type-check)
- ((struct-ref/immediate s n) (read-struct-field n constants) &type-check)
- ((struct-set! s n x) (write-struct-field n constants) &type-check)
- ((struct-set!/immediate s n x) (write-struct-field n constants) &type-check)
- ((struct-vtable s) &type-check))
-
-;; Strings.
-(define-primitive-effects
- ((string-ref s n) (&read-object &string) &type-check)
- ((string-set! s n c) (&write-object &string) &type-check)
- ((number->string _) (&allocate &string) &type-check)
- ((string->number _) (&read-object &string) &type-check)
- ((string-length s) &type-check))
-
-;; Bytevectors.
-(define-primitive-effects
- ((bytevector-length _) &type-check)
-
- ((bv-u8-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s8-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u16-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s16-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-u64-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-s64-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-f32-ref bv n) (&read-object &bytevector) &type-check)
- ((bv-f64-ref bv n) (&read-object &bytevector) &type-check)
-
- ((bv-u8-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s8-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u16-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s16-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-u64-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-s64-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-f32-set! bv n x) (&write-object &bytevector) &type-check)
- ((bv-f64-set! bv n x) (&write-object &bytevector) &type-check))
-
-;; Modules.
-(define-primitive-effects
- ((current-module) (&read-object &module))
- ((cache-current-module! m scope) (&write-object &box))
- ((resolve name bound?) (&read-object &module) &type-check)
- ((cached-toplevel-box scope name bound?) &type-check)
- ((cached-module-box mod name public? bound?) &type-check)
- ((define! name val) (&read-object &module) (&write-object &box)))
-
-;; Numbers.
-(define-primitive-effects
- ((= . _) &type-check)
- ((< . _) &type-check)
- ((> . _) &type-check)
- ((<= . _) &type-check)
- ((>= . _) &type-check)
- ((zero? . _) &type-check)
- ((add . _) &type-check)
- ((mul . _) &type-check)
- ((sub . _) &type-check)
- ((div . _) &type-check)
- ((sub1 . _) &type-check)
- ((add1 . _) &type-check)
- ((quo . _) &type-check)
- ((rem . _) &type-check)
- ((mod . _) &type-check)
- ((complex? _) &type-check)
- ((real? _) &type-check)
- ((rational? _) &type-check)
- ((inf? _) &type-check)
- ((nan? _) &type-check)
- ((integer? _) &type-check)
- ((exact? _) &type-check)
- ((inexact? _) &type-check)
- ((even? _) &type-check)
- ((odd? _) &type-check)
- ((ash n m) &type-check)
- ((logand . _) &type-check)
- ((logior . _) &type-check)
- ((logxor . _) &type-check)
- ((lognot . _) &type-check)
- ((logtest a b) &type-check)
- ((logbit? a b) &type-check)
- ((sqrt _) &type-check)
- ((abs _) &type-check))
-
-;; Characters.
-(define-primitive-effects
- ((char . _) &type-check)
- ((char<=? . _) &type-check)
- ((char>=? . _) &type-check)
- ((char>? . _) &type-check)
- ((integer->char _) &type-check)
- ((char->integer _) &type-check))
-
-(define (primitive-effects constants name args)
- (let ((proc (hashq-ref *primitive-effects* name)))
- (if proc
- (apply proc constants args)
- &all-effects)))
-
-(define (expression-effects exp constants)
- (match exp
- ((or ($ $const) ($ $prim) ($ $values))
- &no-effects)
- ((or ($ $fun) ($ $rec) ($ $closure))
- (&allocate &unknown-memory-kinds))
- (($ $prompt)
- (&write-object &prompt))
- ((or ($ $call) ($ $callk))
- &all-effects)
- (($ $branch k exp)
- (expression-effects exp constants))
- (($ $primcall name args)
- (primitive-effects constants name args))))
-
-(define (compute-effects conts)
- (let ((constants (compute-constant-values conts)))
- (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs names syms ($ $continue k src exp))
- (expression-effects exp constants))
- (($ $kreceive arity kargs)
- (match arity
- (($ $arity _ () #f () #f) &type-check)
- (($ $arity () () _ () #f) (&allocate &pair))
- (($ $arity _ () _ () #f) (logior (&allocate &pair) &type-check))))
- (($ $kfun) &type-check)
- (($ $kclause) &type-check)
- (($ $ktail) &no-effects)))
- conts)))
-
-;; There is a way to abuse effects analysis in CSE to also do scalar
-;; replacement, effectively adding `car' and `cdr' expressions to `cons'
-;; expressions, and likewise with other constructors and setters. This
-;; routine adds appropriate effects to `cons' and `set-car!' and the
-;; like.
-;;
-;; This doesn't affect CSE's ability to eliminate expressions, given
-;; that allocations aren't eliminated anyway, and the new effects will
-;; just cause the allocations not to commute with e.g. set-car! which
-;; is what we want anyway.
-(define (synthesize-definition-effects effects)
- (intmap-map (lambda (label fx)
- (if (logtest (logior &write &allocation) fx)
- (logior fx &read)
- fx))
- effects))
diff --git a/module/language/cps2/elide-values.scm b/module/language/cps2/elide-values.scm
deleted file mode 100644
index ff04789fb..000000000
--- a/module/language/cps2/elide-values.scm
+++ /dev/null
@@ -1,88 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Primcalls that don't correspond to VM instructions are treated as if
-;;; they are calls, and indeed the later reify-primitives pass turns
-;;; them into calls. Because no return arity checking is done for these
-;;; primitives, if a later optimization pass simplifies the primcall to
-;;; a VM operation, the tail of the simplification has to be a
-;;; primcall to 'values. Most of these primcalls can be elided, and
-;;; that is the job of this pass.
-;;;
-;;; Code:
-
-(define-module (language cps2 elide-values)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:export (elide-values))
-
-(define (inline-values cps k src args)
- (match (intmap-ref cps k)
- (($ $ktail)
- (with-cps cps
- (build-term
- ($continue k src ($values args)))))
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (cond
- ((and (not rest) (= (length args) (length req)))
- (with-cps cps
- (build-term
- ($continue kargs src ($values args)))))
- ((and rest (>= (length args) (length req)))
- (let ()
- (define (build-rest cps k tail)
- (match tail
- (()
- (with-cps cps
- (build-term ($continue k src ($const '())))))
- ((v . tail)
- (with-cps cps
- (letv rest)
- (letk krest ($kargs ('rest) (rest)
- ($continue k src ($primcall 'cons (v rest)))))
- ($ (build-rest krest tail))))))
- (with-cps cps
- (letv rest)
- (letk krest ($kargs ('rest) (rest)
- ($continue kargs src
- ($values ,(append (list-head args (length req))
- (list rest))))))
- ($ (build-rest krest (list-tail args (length req)))))))
- (else (with-cps cps #f))))))
-
-(define (elide-values conts)
- (with-fresh-name-state conts
- (persistent-intmap
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs names vars ($ $continue k src ($ $primcall 'values args)))
- (call-with-values (lambda () (inline-values out k src args))
- (lambda (out term)
- (if term
- (let ((cont (build-cont ($kargs names vars ,term))))
- (intmap-replace! out label cont))
- out))))
- (_ out)))
- conts
- conts))))
diff --git a/module/language/cps2/optimize.scm b/module/language/cps2/optimize.scm
deleted file mode 100644
index 9e877b918..000000000
--- a/module/language/cps2/optimize.scm
+++ /dev/null
@@ -1,106 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Optimizations on CPS2.
-;;;
-;;; Code:
-
-(define-module (language cps2 optimize)
- #:use-module (ice-9 match)
- #:use-module (language cps2 constructors)
- #:use-module (language cps2 contification)
- #:use-module (language cps2 cse)
- #:use-module (language cps2 dce)
- #:use-module (language cps2 elide-values)
- #:use-module (language cps2 prune-top-level-scopes)
- #:use-module (language cps2 prune-bailouts)
- #:use-module (language cps2 self-references)
- #:use-module (language cps2 simplify)
- #:use-module (language cps2 specialize-primcalls)
- #:use-module (language cps2 split-rec)
- #:use-module (language cps2 type-fold)
- #:use-module (language cps2 verify)
- #:export (optimize-higher-order-cps
- optimize-first-order-cps))
-
-(define (kw-arg-ref args kw default)
- (match (memq kw args)
- ((_ val . _) val)
- (_ default)))
-
-(define *debug?* #f)
-
-(define (maybe-verify program)
- (if *debug?*
- (verify program)
- program))
-
-(define-syntax-rule (define-optimizer optimize (pass kw default) ...)
- (define* (optimize program #:optional (opts '()))
- ;; This series of assignments to `program' used to be a series of
- ;; let* bindings of `program', as you would imagine. In compiled
- ;; code this is fine because the compiler is able to allocate all
- ;; let*-bound variable to the same slot, which also means that the
- ;; garbage collector doesn't have to retain so many copies of the
- ;; term being optimized. However during bootstrap, the interpreter
- ;; doesn't do this optimization, leading to excessive data retention
- ;; as the terms are rewritten. To marginally improve bootstrap
- ;; memory usage, here we use set! instead. The compiler should
- ;; produce the same code in any case, though currently it does not
- ;; because it doesn't do escape analysis on the box created for the
- ;; set!.
- (maybe-verify program)
- (set! program
- (if (kw-arg-ref opts kw default)
- (maybe-verify (pass program))
- program))
- ...
- (verify program)
- program))
-
-;; Passes that are needed:
-;;
-;; * Abort contification: turning abort primcalls into continuation
-;; calls, and eliding prompts if possible.
-;;
-;; * Loop peeling. Unrolls the first round through a loop if the
-;; loop has effects that CSE can work on. Requires effects
-;; analysis. When run before CSE, loop peeling is the equivalent
-;; of loop-invariant code motion (LICM).
-;;
-(define-optimizer optimize-higher-order-cps
- (split-rec #:split-rec? #t)
- (eliminate-dead-code #:eliminate-dead-code? #t)
- (prune-top-level-scopes #:prune-top-level-scopes? #t)
- (simplify #:simplify? #t)
- (contify #:contify? #t)
- (inline-constructors #:inline-constructors? #t)
- (specialize-primcalls #:specialize-primcalls? #t)
- (elide-values #:elide-values? #t)
- (prune-bailouts #:prune-bailouts? #t)
- (eliminate-common-subexpressions #:cse? #t)
- (type-fold #:type-fold? #t)
- (resolve-self-references #:resolve-self-references? #t)
- (eliminate-dead-code #:eliminate-dead-code? #t)
- (simplify #:simplify? #t))
-
-(define-optimizer optimize-first-order-cps
- (eliminate-dead-code #:eliminate-dead-code? #t)
- (simplify #:simplify? #t))
diff --git a/module/language/cps2/prune-bailouts.scm b/module/language/cps2/prune-bailouts.scm
deleted file mode 100644
index f33d2aeb4..000000000
--- a/module/language/cps2/prune-bailouts.scm
+++ /dev/null
@@ -1,86 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass that prunes successors of expressions that bail out.
-;;;
-;;; Code:
-
-(define-module (language cps2 prune-bailouts)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (prune-bailouts))
-
-(define (compute-tails conts)
- "For each LABEL->CONT entry in the intmap CONTS, compute a
-LABEL->TAIL-LABEL indicating the tail continuation of each expression's
-containing function. In some cases TAIL-LABEL might not be available,
-for example if there is a stale $kfun pointing at a body, or for
-unreferenced terms. In that case TAIL-LABEL is either absent or #f."
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kfun src meta self tail clause)
- (intset-fold (lambda (label out)
- (intmap-add out label tail (lambda (old new) #f)))
- (compute-function-body conts label)
- out))
- (_ out)))
- conts
- empty-intmap))
-
-(define (prune-bailout out tails k src exp)
- (match (intmap-ref out k)
- (($ $ktail)
- (with-cps out #f))
- (_
- (match (intmap-ref tails k (lambda (_) #f))
- (#f
- (with-cps out #f))
- (ktail
- (with-cps out
- (letv prim rest)
- (letk kresult ($kargs ('rest) (rest)
- ($continue ktail src ($values ()))))
- (letk kreceive ($kreceive '() 'rest kresult))
- (build-term ($continue kreceive src ,exp))))))))
-
-(define (prune-bailouts conts)
- (let ((tails (compute-tails conts)))
- (with-fresh-name-state conts
- (persistent-intmap
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs names vars
- ($ $continue k src
- (and exp ($ $primcall (or 'error 'scm-error 'throw)))))
- (call-with-values (lambda () (prune-bailout out tails k src exp))
- (lambda (out term)
- (if term
- (let ((cont (build-cont ($kargs names vars ,term))))
- (intmap-replace! out label cont))
- out))))
- (_ out)))
- conts
- conts)))))
diff --git a/module/language/cps2/prune-top-level-scopes.scm b/module/language/cps2/prune-top-level-scopes.scm
deleted file mode 100644
index c737534b0..000000000
--- a/module/language/cps2/prune-top-level-scopes.scm
+++ /dev/null
@@ -1,63 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2014, 2015 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:
-;;;
-;;; A simple pass to prune unneeded top-level scopes.
-;;;
-;;; Code:
-
-(define-module (language cps2 prune-top-level-scopes)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (prune-top-level-scopes))
-
-(define (compute-used-scopes conts constants)
- (persistent-intset
- (intmap-fold
- (lambda (label cont used-scopes)
- (match cont
- (($ $kargs _ _
- ($ $continue k src
- ($ $primcall 'cached-toplevel-box (scope name bound?))))
- (intset-add! used-scopes (intmap-ref constants scope)))
- (_
- used-scopes)))
- conts
- empty-intset)))
-
-(define (prune-top-level-scopes conts)
- (let* ((constants (compute-constant-values conts))
- (used-scopes (compute-used-scopes conts constants)))
- (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs names vars
- ($ $continue k src
- ($ $primcall 'cache-current-module!
- (module (? (lambda (scope)
- (let ((val (intmap-ref constants scope)))
- (not (intset-ref used-scopes val)))))))))
- (build-cont ($kargs names vars
- ($continue k src ($values ())))))
- (_
- cont)))
- conts)))
diff --git a/module/language/cps2/reify-primitives.scm b/module/language/cps2/reify-primitives.scm
deleted file mode 100644
index 55409bfc1..000000000
--- a/module/language/cps2/reify-primitives.scm
+++ /dev/null
@@ -1,167 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass to reify lone $prim's that were never folded into a
-;;; $primcall, and $primcall's to primitives that don't have a
-;;; corresponding VM op.
-;;;
-;;; Code:
-
-(define-module (language cps2 reify-primitives)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps primitives)
- #:use-module (language cps intmap)
- #:use-module (language bytecode)
- #:export (reify-primitives))
-
-(define (module-box cps src module name public? bound? val-proc)
- (with-cps cps
- (letv box)
- (let$ body (val-proc box))
- (letk kbox ($kargs ('box) (box) ,body))
- ($ (with-cps-constants ((module module)
- (name name)
- (public? public?)
- (bound? bound?))
- (build-term ($continue kbox src
- ($primcall 'cached-module-box
- (module name public? bound?))))))))
-
-(define (primitive-module name)
- (case name
- ((bytevector?
- bytevector-length
-
- bytevector-u8-ref bytevector-u8-set!
- bytevector-s8-ref bytevector-s8-set!
-
- bytevector-u16-ref bytevector-u16-set!
- bytevector-u16-native-ref bytevector-u16-native-set!
- bytevector-s16-ref bytevector-s16-set!
- bytevector-s16-native-ref bytevector-s16-native-set!
-
- bytevector-u32-ref bytevector-u32-set!
- bytevector-u32-native-ref bytevector-u32-native-set!
- bytevector-s32-ref bytevector-s32-set!
- bytevector-s32-native-ref bytevector-s32-native-set!
-
- bytevector-u64-ref bytevector-u64-set!
- bytevector-u64-native-ref bytevector-u64-native-set!
- bytevector-s64-ref bytevector-s64-set!
- bytevector-s64-native-ref bytevector-s64-native-set!
-
- bytevector-ieee-single-ref bytevector-ieee-single-set!
- bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
- bytevector-ieee-double-ref bytevector-ieee-double-set!
- bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!)
- '(rnrs bytevectors))
- ((class-of) '(oop goops))
- (else '(guile))))
-
-(define (primitive-ref cps name k src)
- (module-box cps src (primitive-module name) name #f #t
- (lambda (cps box)
- (with-cps cps
- (build-term
- ($continue k src ($primcall 'box-ref (box))))))))
-
-(define (builtin-ref cps idx k src)
- (with-cps cps
- ($ (with-cps-constants ((idx idx))
- (build-term
- ($continue k src ($primcall 'builtin-ref (idx))))))))
-
-(define (reify-clause cps ktail)
- (with-cps cps
- (letv throw)
- (let$ throw-body
- (with-cps-constants ((wna 'wrong-number-of-args)
- (false #f)
- (str "Wrong number of arguments")
- (eol '()))
- (build-term
- ($continue ktail #f
- ($call throw (wna false str eol false))))))
- (letk kthrow ($kargs ('throw) (throw) ,throw-body))
- (let$ body (primitive-ref 'throw kthrow #f))
- (letk kbody ($kargs () () ,body))
- (letk kclause ($kclause ('() '() #f '() #f) kbody #f))
- kclause))
-
-;; A $kreceive continuation should have only one predecessor.
-(define (uniquify-receive cps k)
- (match (intmap-ref cps k)
- (($ $kreceive ($ $arity req () rest () #f) kargs)
- (with-cps cps
- (letk k ($kreceive req rest kargs))
- k))
- (_
- (with-cps cps k))))
-
-(define (reify-primitives cps)
- (define (visit-cont label cont cps)
- (define (resolve-prim cps name k src)
- (cond
- ((builtin-name->index name)
- => (lambda (idx) (builtin-ref cps idx k src)))
- (else
- (primitive-ref cps name k src))))
- (match cont
- (($ $kfun src meta self tail #f)
- (with-cps cps
- (let$ clause (reify-clause tail))
- (setk label ($kfun src meta self tail clause))))
- (($ $kargs names vars ($ $continue k src ($ $prim name)))
- (with-cps cps
- (let$ k (uniquify-receive k))
- (let$ body (resolve-prim name k src))
- (setk label ($kargs names vars ,body))))
- (($ $kargs names vars
- ($ $continue k src ($ $primcall 'call-thunk/no-inline (proc))))
- (with-cps cps
- (setk label ($kargs names vars ($continue k src ($call proc ()))))))
- (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
- (if (or (prim-instruction name) (branching-primitive? name))
- ;; Assume arities are correct.
- cps
- (with-cps cps
- (letv proc)
- (let$ k (uniquify-receive k))
- (letk kproc ($kargs ('proc) (proc)
- ($continue k src ($call proc args))))
- (let$ body (resolve-prim name kproc src))
- (setk label ($kargs names vars ,body)))))
- (($ $kargs names vars ($ $continue k src ($ $call proc args)))
- (with-cps cps
- (let$ k (uniquify-receive k))
- (setk label ($kargs names vars
- ($continue k src ($call proc args))))))
- (($ $kargs names vars ($ $continue k src ($ $callk k* proc args)))
- (with-cps cps
- (let$ k (uniquify-receive k))
- (setk label ($kargs names vars
- ($continue k src ($callk k* proc args))))))
- (_ cps)))
-
- (with-fresh-name-state cps
- (persistent-intmap (intmap-fold visit-cont cps cps))))
diff --git a/module/language/cps2/renumber.scm b/module/language/cps2/renumber.scm
deleted file mode 100644
index 16ed29ced..000000000
--- a/module/language/cps2/renumber.scm
+++ /dev/null
@@ -1,217 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass to renumber variables and continuation labels so that they
-;;; are contiguous within each function and, in the case of labels,
-;;; topologically sorted.
-;;;
-;;; Code:
-
-(define-module (language cps2 renumber)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intset)
- #:use-module (language cps intmap)
- #:export (renumber))
-
-(define* (compute-tail-path-lengths conts kfun preds)
- (define (add-lengths labels lengths length)
- (intset-fold (lambda (label lengths)
- (intmap-add! lengths label length))
- labels
- lengths))
- (define (compute-next labels lengths)
- (intset-fold (lambda (label labels)
- (fold1 (lambda (pred labels)
- (if (intmap-ref lengths pred (lambda (_) #f))
- labels
- (intset-add! labels pred)))
- (intmap-ref preds label)
- labels))
- labels
- empty-intset))
- (define (visit labels lengths length)
- (let ((lengths (add-lengths labels lengths length)))
- (values (compute-next labels lengths) lengths (1+ length))))
- (match (intmap-ref conts kfun)
- (($ $kfun src meta self tail clause)
- (worklist-fold visit (intset-add empty-intset tail) empty-intmap 0))))
-
-;; Topologically sort the continuation tree starting at k0, using
-;; reverse post-order numbering.
-(define (sort-labels-locally conts k0 path-lengths)
- (define (visit-kf-first? kf kt)
- ;; Visit the successor of a branch with the shortest path length to
- ;; the tail first, so that if the branches are unsorted, the longer
- ;; path length will appear first. This will move a loop exit out of
- ;; a loop.
- (let ((kf-len (intmap-ref path-lengths kf (lambda (_) #f)))
- (kt-len (intmap-ref path-lengths kt (lambda (_) #f))))
- (if kt-len
- (or (not kf-len) (< kf-len kt-len)
- ;; If the path lengths are the same, preserve original
- ;; order to avoid squirreliness.
- (and (= kf-len kt-len) (< kt kf)))
- (if kf-len #f (< kt kf)))))
- (let ((order '())
- (visited empty-intset))
- (let visit ((k k0) (order '()) (visited empty-intset))
- (define (visit2 k0 k1 order visited)
- (let-values (((order visited) (visit k0 order visited)))
- (visit k1 order visited)))
- (if (intset-ref visited k)
- (values order visited)
- (let ((visited (intset-add visited k)))
- (call-with-values
- (lambda ()
- (match (intmap-ref conts k)
- (($ $kargs names syms ($ $continue k src exp))
- (match exp
- (($ $prompt escape? tag handler)
- (visit2 k handler order visited))
- (($ $branch kt)
- (if (visit-kf-first? k kt)
- (visit2 k kt order visited)
- (visit2 kt k order visited)))
- (_
- (visit k order visited))))
- (($ $kreceive arity k) (visit k order visited))
- (($ $kclause arity kbody kalt)
- (if kalt
- (visit2 kalt kbody order visited)
- (visit kbody order visited)))
- (($ $kfun src meta self tail clause)
- (if clause
- (visit2 tail clause order visited)
- (visit tail order visited)))
- (($ $ktail) (values order visited))))
- (lambda (order visited)
- ;; Add k to the reverse post-order.
- (values (cons k order) visited))))))))
-
-(define (compute-renaming conts kfun)
- ;; labels := old -> new
- ;; vars := old -> new
- (define *next-label* -1)
- (define *next-var* -1)
- (define (rename-label label labels)
- (set! *next-label* (1+ *next-label*))
- (intmap-add! labels label *next-label*))
- (define (rename-var sym vars)
- (set! *next-var* (1+ *next-var*))
- (intmap-add! vars sym *next-var*))
- (define (rename label labels vars)
- (values (rename-label label labels)
- (match (intmap-ref conts label)
- (($ $kargs names syms exp)
- (fold1 rename-var syms vars))
- (($ $kfun src meta self tail clause)
- (rename-var self vars))
- (_ vars))))
- (define (maybe-visit-fun kfun labels vars)
- (if (intmap-ref labels kfun (lambda (_) #f))
- (values labels vars)
- (visit-fun kfun labels vars)))
- (define (visit-nested-funs k labels vars)
- (match (intmap-ref conts k)
- (($ $kargs names syms ($ $continue k src ($ $fun kfun)))
- (visit-fun kfun labels vars))
- (($ $kargs names syms ($ $continue k src ($ $rec names* syms*
- (($ $fun kfun) ...))))
- (fold2 visit-fun kfun labels vars))
- (($ $kargs names syms ($ $continue k src ($ $closure kfun nfree)))
- ;; Closures with zero free vars get copy-propagated so it's
- ;; possible to already have visited them.
- (maybe-visit-fun kfun labels vars))
- (($ $kargs names syms ($ $continue k src ($ $callk kfun)))
- ;; Well-known functions never have a $closure created for them
- ;; and are only referenced by their $callk call sites.
- (maybe-visit-fun kfun labels vars))
- (_ (values labels vars))))
- (define (visit-fun kfun labels vars)
- (let* ((preds (compute-predecessors conts kfun))
- (path-lengths (compute-tail-path-lengths conts kfun preds))
- (order (sort-labels-locally conts kfun path-lengths)))
- ;; First rename locally, then recurse on nested functions.
- (let-values (((labels vars) (fold2 rename order labels vars)))
- (fold2 visit-nested-funs order labels vars))))
- (let-values (((labels vars) (visit-fun kfun empty-intmap empty-intmap)))
- (values (persistent-intmap labels) (persistent-intmap vars))))
-
-(define* (renumber conts #:optional (kfun 0))
- (let-values (((label-map var-map) (compute-renaming conts kfun)))
- (define (rename-label label) (intmap-ref label-map label))
- (define (rename-var var) (intmap-ref var-map var))
- (define (rename-exp exp)
- (rewrite-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $closure k nfree)
- ($closure (rename-label k) nfree))
- (($ $fun body)
- ($fun (rename-label body)))
- (($ $rec names vars funs)
- ($rec names (map rename-var vars) (map rename-exp funs)))
- (($ $values args)
- ($values ,(map rename-var args)))
- (($ $call proc args)
- ($call (rename-var proc) ,(map rename-var args)))
- (($ $callk k proc args)
- ($callk (rename-label k) (rename-var proc) ,(map rename-var args)))
- (($ $branch kt exp)
- ($branch (rename-label kt) ,(rename-exp exp)))
- (($ $primcall name args)
- ($primcall name ,(map rename-var args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (rename-var tag) (rename-label handler)))))
- (define (rename-arity arity)
- (match arity
- (($ $arity req opt rest () aok?)
- arity)
- (($ $arity req opt rest kw aok?)
- (match kw
- (() arity)
- (((kw kw-name kw-var) ...)
- (let ((kw (map list kw kw-name (map rename-var kw-var))))
- (make-$arity req opt rest kw aok?)))))))
- (persistent-intmap
- (intmap-fold
- (lambda (old-k new-k out)
- (intmap-add!
- out
- new-k
- (rewrite-cont (intmap-ref conts old-k)
- (($ $kargs names syms ($ $continue k src exp))
- ($kargs names (map rename-var syms)
- ($continue (rename-label k) src ,(rename-exp exp))))
- (($ $kreceive ($ $arity req () rest () #f) k)
- ($kreceive req rest (rename-label k)))
- (($ $ktail)
- ($ktail))
- (($ $kfun src meta self tail clause)
- ($kfun src meta (rename-var self) (rename-label tail)
- (and clause (rename-label clause))))
- (($ $kclause arity body alternate)
- ($kclause ,(rename-arity arity) (rename-label body)
- (and alternate (rename-label alternate)))))))
- label-map
- empty-intmap))))
diff --git a/module/language/cps2/self-references.scm b/module/language/cps2/self-references.scm
deleted file mode 100644
index 20ac56f39..000000000
--- a/module/language/cps2/self-references.scm
+++ /dev/null
@@ -1,79 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A pass that replaces free references to recursive functions with
-;;; bound references.
-;;;
-;;; Code:
-
-(define-module (language cps2 self-references)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold))
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (resolve-self-references))
-
-(define* (resolve-self-references cps #:optional (label 0) (env empty-intmap))
- (define (subst var)
- (intmap-ref env var (lambda (var) var)))
-
- (define (rename-exp label cps names vars k src exp)
- (let ((exp (rewrite-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $call proc args)
- ($call (subst proc) ,(map subst args)))
- (($ $callk k proc args)
- ($callk k (subst proc) ,(map subst args)))
- (($ $primcall name args)
- ($primcall name ,(map subst args)))
- (($ $branch k ($ $values (arg)))
- ($branch k ($values ((subst arg)))))
- (($ $branch k ($ $primcall name args))
- ($branch k ($primcall name ,(map subst args))))
- (($ $values args)
- ($values ,(map subst args)))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst tag) handler)))))
- (intmap-replace! cps label
- (build-cont
- ($kargs names vars ($continue k src ,exp))))))
-
- (define (visit-exp cps label names vars k src exp)
- (match exp
- (($ $fun label)
- (resolve-self-references cps label env))
- (($ $rec names vars (($ $fun labels) ...))
- (fold (lambda (label var cps)
- (match (intmap-ref cps label)
- (($ $kfun src meta self)
- (resolve-self-references cps label
- (intmap-add env var self)))))
- cps labels vars))
- (_ (rename-exp label cps names vars k src exp))))
-
- (intset-fold (lambda (label cps)
- (match (intmap-ref cps label)
- (($ $kargs names vars ($ $continue k src exp))
- (visit-exp cps label names vars k src exp))
- (_ cps)))
- (compute-function-body cps label)
- cps))
diff --git a/module/language/cps2/simplify.scm b/module/language/cps2/simplify.scm
deleted file mode 100644
index 19d7a1799..000000000
--- a/module/language/cps2/simplify.scm
+++ /dev/null
@@ -1,267 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; The fundamental lambda calculus reductions, like beta and eta
-;;; reduction and so on. Pretty lame currently.
-;;;
-;;; Code:
-
-(define-module (language cps2 simplify)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-26)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intset)
- #:use-module (language cps intmap)
- #:export (simplify))
-
-(define (intset-maybe-add! set k add?)
- (if add? (intset-add! set k) set))
-
-(define (intset-add* set k*)
- (let lp ((set set) (k* k*))
- (match k*
- ((k . k*) (lp (intset-add set k) k*))
- (() set))))
-
-(define (intset-add*! set k*)
- (fold1 (lambda (k set) (intset-add! set k)) k* set))
-
-(define (fold2* f l1 l2 seed)
- (let lp ((l1 l1) (l2 l2) (seed seed))
- (match (cons l1 l2)
- ((() . ()) seed)
- (((x1 . l1) . (x2 . l2)) (lp l1 l2 (f x1 x2 seed))))))
-
-(define (transform-conts f conts)
- (persistent-intmap
- (intmap-fold (lambda (k v out)
- (let ((v* (f k v)))
- (cond
- ((equal? v v*) out)
- (v* (intmap-replace! out k v*))
- (else (intmap-remove out k)))))
- conts
- conts)))
-
-(define (compute-singly-referenced-vars conts)
- (define (visit label cont single multiple)
- (define (add-ref var single multiple)
- (if (intset-ref single var)
- (values single (intset-add! multiple var))
- (values (intset-add! single var) multiple)))
- (define (ref var) (add-ref var single multiple))
- (define (ref* vars) (fold2 add-ref vars single multiple))
- (match cont
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
- (values single multiple))
- (($ $call proc args)
- (ref* (cons proc args)))
- (($ $callk k proc args)
- (ref* (cons proc args)))
- (($ $primcall name args)
- (ref* args))
- (($ $values args)
- (ref* args))
- (($ $branch kt ($ $values (var)))
- (ref var))
- (($ $branch kt ($ $primcall name args))
- (ref* args))
- (($ $prompt escape? tag handler)
- (ref tag))))
- (_
- (values single multiple))))
- (let*-values (((single multiple) (values empty-intset empty-intset))
- ((single multiple) (intmap-fold visit conts single multiple)))
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple))))
-
-;;; Continuations whose values are simply forwarded to another and not
-;;; used in any other way may be elided via eta reduction over labels.
-;;;
-;;; There is an exception however: we must exclude strongly-connected
-;;; components (SCCs). The only kind of SCC we can build out of $values
-;;; expressions are infinite loops.
-;;;
-;;; Condition A below excludes single-node SCCs. Single-node SCCs
-;;; cannot be reduced.
-;;;
-;;; Condition B conservatively excludes edges to labels already marked
-;;; as candidates. This prevents back-edges and so breaks SCCs, and is
-;;; optimal if labels are sorted. If the labels aren't sorted it's
-;;; suboptimal but cheap.
-(define (compute-eta-reductions conts kfun)
- (let ((singly-used (compute-singly-referenced-vars conts)))
- (define (singly-used? vars)
- (match vars
- (() #t)
- ((var . vars)
- (and (intset-ref singly-used var) (singly-used? vars)))))
- (define (visit-fun kfun body eta)
- (define (visit-cont label eta)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src ($ $values vars)))
- (intset-maybe-add! eta label
- (match (intmap-ref conts k)
- (($ $kargs)
- (and (not (eqv? label k)) ; A
- (not (intset-ref eta label)) ; B
- (singly-used? vars)))
- (_ #f))))
- (_
- eta)))
- (intset-fold visit-cont body eta))
- (persistent-intset
- (intmap-fold visit-fun
- (compute-reachable-functions conts kfun)
- empty-intset))))
-
-(define (eta-reduce conts kfun)
- (let ((label-set (compute-eta-reductions conts kfun)))
- ;; Replace any continuation to a label in LABEL-SET with the label's
- ;; continuation. The label will denote a $kargs continuation, so
- ;; only terms that can continue to $kargs need be taken into
- ;; account.
- (define (subst label)
- (if (intset-ref label-set label)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue k)) (subst k)))
- label))
- (transform-conts
- (lambda (label cont)
- (and (not (intset-ref label-set label))
- (rewrite-cont cont
- (($ $kargs names syms ($ $continue kf src ($ $branch kt exp)))
- ($kargs names syms
- ($continue (subst kf) src ($branch (subst kt) ,exp))))
- (($ $kargs names syms ($ $continue k src exp))
- ($kargs names syms
- ($continue (subst k) src ,exp)))
- (($ $kreceive ($ $arity req () rest () #f) k)
- ($kreceive req rest (subst k)))
- (($ $kclause arity body alt)
- ($kclause ,arity (subst body) alt))
- (_ ,cont))))
- conts)))
-
-(define (compute-singly-referenced-labels conts body)
- (define (add-ref label single multiple)
- (define (ref k single multiple)
- (if (intset-ref single k)
- (values single (intset-add! multiple k))
- (values (intset-add! single k) multiple)))
- (define (ref0) (values single multiple))
- (define (ref1 k) (ref k single multiple))
- (define (ref2 k k*)
- (if k*
- (let-values (((single multiple) (ref k single multiple)))
- (ref k* single multiple))
- (ref1 k)))
- (match (intmap-ref conts label)
- (($ $kreceive arity k) (ref1 k))
- (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
- (($ $ktail) (ref0))
- (($ $kclause arity kbody kalt) (ref2 kbody kalt))
- (($ $kargs names syms ($ $continue k src exp))
- (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
- (let*-values (((single multiple) (values empty-intset empty-intset))
- ((single multiple) (intset-fold add-ref body single multiple)))
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple))))
-
-(define (compute-beta-reductions conts kfun)
- (define (visit-fun kfun body beta)
- (let ((single (compute-singly-referenced-labels conts body)))
- (define (visit-cont label beta)
- (match (intmap-ref conts label)
- ;; A continuation's body can be inlined in place of a $values
- ;; expression if the continuation is a $kargs. It should only
- ;; be inlined if it is used only once, and not recursively.
- (($ $kargs _ _ ($ $continue k src ($ $values)))
- (intset-maybe-add! beta label
- (and (intset-ref single k)
- (match (intmap-ref conts k)
- (($ $kargs) #t)
- (_ #f)))))
- (_
- beta)))
- (intset-fold visit-cont body beta)))
- (persistent-intset
- (intmap-fold visit-fun
- (compute-reachable-functions conts kfun)
- empty-intset)))
-
-(define (compute-beta-var-substitutions conts label-set)
- (define (add-var-substs label var-map)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue k _ ($ $values vals)))
- (match (intmap-ref conts k)
- (($ $kargs names vars)
- (fold2* (lambda (var val var-map)
- (intmap-add! var-map var val))
- vars vals var-map))))))
- (intset-fold add-var-substs label-set empty-intmap))
-
-(define (beta-reduce conts kfun)
- (let* ((label-set (compute-beta-reductions conts kfun))
- (var-map (compute-beta-var-substitutions conts label-set)))
- (define (subst var)
- (match (intmap-ref var-map var (lambda (_) #f))
- (#f var)
- (val (subst val))))
- (define (transform-exp label k src exp)
- (if (intset-ref label-set label)
- (match (intmap-ref conts k)
- (($ $kargs _ _ ($ $continue k* src* exp*))
- (transform-exp k k* src* exp*)))
- (build-term
- ($continue k src
- ,(rewrite-exp exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
- ,exp)
- (($ $call proc args)
- ($call (subst proc) ,(map subst args)))
- (($ $callk k proc args)
- ($callk k (subst proc) ,(map subst args)))
- (($ $primcall name args)
- ($primcall name ,(map subst args)))
- (($ $values args)
- ($values ,(map subst args)))
- (($ $branch kt ($ $values (var)))
- ($branch kt ($values ((subst var)))))
- (($ $branch kt ($ $primcall name args))
- ($branch kt ($primcall name ,(map subst args))))
- (($ $prompt escape? tag handler)
- ($prompt escape? (subst tag) handler)))))))
- (transform-conts
- (lambda (label cont)
- (match cont
- (($ $kargs names syms ($ $continue k src exp))
- (build-cont
- ($kargs names syms ,(transform-exp label k src exp))))
- (_ cont)))
- conts)))
-
-(define (simplify conts)
- (eta-reduce (beta-reduce conts 0) 0))
diff --git a/module/language/cps2/slot-allocation.scm b/module/language/cps2/slot-allocation.scm
deleted file mode 100644
index 48f5a1fd3..000000000
--- a/module/language/cps2/slot-allocation.scm
+++ /dev/null
@@ -1,995 +0,0 @@
-;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; A module to assign stack slots to variables in a CPS term.
-;;;
-;;; Code:
-
-(define-module (language cps2 slot-allocation)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-9)
- #:use-module (srfi srfi-11)
- #:use-module (srfi srfi-26)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (allocate-slots
- lookup-slot
- lookup-maybe-slot
- lookup-constant-value
- lookup-maybe-constant-value
- lookup-nlocals
- lookup-call-proc-slot
- lookup-parallel-moves
- lookup-dead-slot-map))
-
-(define-record-type $allocation
- (make-allocation slots constant-values call-allocs shuffles frame-sizes)
- allocation?
-
- ;; A map of VAR to slot allocation. A slot allocation is an integer,
- ;; if the variable has been assigned a slot.
- ;;
- (slots allocation-slots)
-
- ;; A map of VAR to constant value, for variables with constant values.
- ;;
- (constant-values allocation-constant-values)
-
- ;; A map of LABEL to /call allocs/, for expressions that continue to
- ;; $kreceive continuations: non-tail calls and $prompt expressions.
- ;;
- ;; A call alloc contains two pieces of information: the call's /proc
- ;; slot/ and a /dead slot map/. The proc slot indicates the slot of a
- ;; procedure in a procedure call, or where the procedure would be in a
- ;; multiple-value return.
- ;;
- ;; The dead slot map indicates, what slots should be ignored by GC
- ;; when marking the frame. A dead slot map is a bitfield, as an
- ;; integer.
- ;;
- (call-allocs allocation-call-allocs)
-
- ;; A map of LABEL to /parallel moves/. Parallel moves shuffle locals
- ;; into position for a $call, $callk, or $values, or shuffle returned
- ;; values back into place in a $kreceive.
- ;;
- ;; A set of moves is expressed as an ordered list of (SRC . DST)
- ;; moves, where SRC and DST are slots. This may involve a temporary
- ;; variable.
- ;;
- (shuffles allocation-shuffles)
-
- ;; The number of locals for a $kclause.
- ;;
- (frame-sizes allocation-frame-sizes))
-
-(define-record-type $call-alloc
- (make-call-alloc proc-slot dead-slot-map)
- call-alloc?
- (proc-slot call-alloc-proc-slot)
- (dead-slot-map call-alloc-dead-slot-map))
-
-(define (lookup-maybe-slot var allocation)
- (intmap-ref (allocation-slots allocation) var (lambda (_) #f)))
-
-(define (lookup-slot var allocation)
- (intmap-ref (allocation-slots allocation) var))
-
-(define *absent* (list 'absent))
-
-(define (lookup-constant-value var allocation)
- (let ((value (intmap-ref (allocation-constant-values allocation) var
- (lambda (_) *absent*))))
- (when (eq? value *absent*)
- (error "Variable does not have constant value" var))
- value))
-
-(define (lookup-maybe-constant-value var allocation)
- (let ((value (intmap-ref (allocation-constant-values allocation) var
- (lambda (_) *absent*))))
- (if (eq? value *absent*)
- (values #f #f)
- (values #t value))))
-
-(define (lookup-call-alloc k allocation)
- (intmap-ref (allocation-call-allocs allocation) k))
-
-(define (lookup-call-proc-slot k allocation)
- (or (call-alloc-proc-slot (lookup-call-alloc k allocation))
- (error "Call has no proc slot" k)))
-
-(define (lookup-parallel-moves k allocation)
- (intmap-ref (allocation-shuffles allocation) k))
-
-(define (lookup-dead-slot-map k allocation)
- (or (call-alloc-dead-slot-map (lookup-call-alloc k allocation))
- (error "Call has no dead slot map" k)))
-
-(define (lookup-nlocals k allocation)
- (intmap-ref (allocation-frame-sizes allocation) k))
-
-(define (intset-pop set)
- (match (intset-next set)
- (#f (values set #f))
- (i (values (intset-remove set i) i))))
-
-(define (solve-flow-equations succs in out kill gen subtract add meet)
- "Find a fixed point for flow equations for SUCCS, where IN and OUT are
-the initial conditions as intmaps with one key for every node in SUCCS.
-KILL and GEN are intmaps indicating the state that is killed or defined
-at every node, and SUBTRACT, ADD, and MEET operates on that state."
- (define (visit label in out)
- (let* ((in-1 (intmap-ref in label))
- (kill-1 (intmap-ref kill label))
- (gen-1 (intmap-ref gen label))
- (out-1 (intmap-ref out label))
- (out-1* (add (subtract in-1 kill-1) gen-1)))
- (if (eq? out-1 out-1*)
- (values empty-intset in out)
- (let ((out (intmap-replace! out label out-1*)))
- (call-with-values
- (lambda ()
- (intset-fold (lambda (succ in changed)
- (let* ((in-1 (intmap-ref in succ))
- (in-1* (meet in-1 out-1*)))
- (if (eq? in-1 in-1*)
- (values in changed)
- (values (intmap-replace! in succ in-1*)
- (intset-add changed succ)))))
- (intmap-ref succs label) in empty-intset))
- (lambda (in changed)
- (values changed in out)))))))
-
- (let run ((worklist (intmap-keys succs)) (in in) (out out))
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist popped)
- (if popped
- (call-with-values (lambda () (visit popped in out))
- (lambda (changed in out)
- (run (intset-union worklist changed) in out)))
- (values (persistent-intmap in)
- (persistent-intmap out)))))))
-
-(define-syntax-rule (persistent-intmap2 exp)
- (call-with-values (lambda () exp)
- (lambda (a b)
- (values (persistent-intmap a) (persistent-intmap b)))))
-
-(define (compute-defs-and-uses cps)
- "Return two LABEL->VAR... maps indicating values defined at and used
-by a label, respectively."
- (define (vars->intset vars)
- (fold (lambda (var set) (intset-add set var)) empty-intset vars))
- (persistent-intmap2
- (intmap-fold
- (lambda (label cont defs uses)
- (define (get-defs k)
- (match (intmap-ref cps k)
- (($ $kargs names vars) (vars->intset vars))
- (_ empty-intset)))
- (define (return d u)
- (values (intmap-add! defs label d)
- (intmap-add! uses label u)))
- (match cont
- (($ $kfun src meta self)
- (return (intset self) empty-intset))
- (($ $kargs _ _ ($ $continue k src exp))
- (match exp
- ((or ($ $const) ($ $closure))
- (return (get-defs k) empty-intset))
- (($ $call proc args)
- (return (get-defs k) (intset-add (vars->intset args) proc)))
- (($ $callk _ proc args)
- (return (get-defs k) (intset-add (vars->intset args) proc)))
- (($ $primcall name args)
- (return (get-defs k) (vars->intset args)))
- (($ $branch kt ($ $primcall name args))
- (return empty-intset (vars->intset args)))
- (($ $branch kt ($ $values args))
- (return empty-intset (vars->intset args)))
- (($ $values args)
- (return (get-defs k) (vars->intset args)))
- (($ $prompt escape? tag handler)
- (return empty-intset (intset tag)))))
- (($ $kclause arity body alt)
- (return (get-defs body) empty-intset))
- (($ $kreceive arity kargs)
- (return (get-defs kargs) empty-intset))
- (($ $ktail)
- (return empty-intset empty-intset))))
- cps
- empty-intmap
- empty-intmap)))
-
-(define (compute-reverse-control-flow-order preds)
- "Return a LABEL->ORDER bijection where ORDER is a contiguous set of
-integers starting from 0 and incrementing in sort order."
- ;; This is more involved than forward control flow because not all
- ;; live labels are reachable from the tail.
- (persistent-intmap
- (fold2 (lambda (component order n)
- (intset-fold (lambda (label order n)
- (values (intmap-add! order label n)
- (1+ n)))
- component order n))
- (reverse (compute-sorted-strongly-connected-components preds))
- empty-intmap 0)))
-
-(define* (add-prompt-control-flow-edges conts succs #:key complete?)
- "For all prompts in DFG in the range [MIN-LABEL, MIN-LABEL +
-LABEL-COUNT), invoke F with arguments PROMPT, HANDLER, and BODY for each
-body continuation in the prompt."
- (define (intset-filter pred set)
- (intset-fold (lambda (i set)
- (if (pred i) set (intset-remove set i)))
- set
- set))
- (define (intset-any pred set)
- (intset-fold (lambda (i res)
- (if (or res (pred i)) #t res))
- set
- #f))
- (define (visit-prompt label handler succs)
- ;; FIXME: It isn't correct to use all continuations reachable from
- ;; the prompt, because that includes continuations outside the
- ;; prompt body. This point is moot if the handler's control flow
- ;; joins with the the body, as is usually but not always the case.
- ;;
- ;; One counter-example is when the handler contifies an infinite
- ;; loop; in that case we compute a too-large prompt body. This
- ;; error is currently innocuous, but we should fix it at some point.
- ;;
- ;; The fix is to end the body at the corresponding "pop" primcall,
- ;; if any.
- (let ((body (intset-subtract (compute-function-body conts label)
- (compute-function-body conts handler))))
- (define (out-or-back-edge? label)
- ;; Most uses of visit-prompt-control-flow don't need every body
- ;; continuation, and would be happy getting called only for
- ;; continuations that postdominate the rest of the body. Unless
- ;; you pass #:complete? #t, we only invoke F on continuations
- ;; that can leave the body, or on back-edges in loops.
- ;;
- ;; You would think that looking for the final "pop" primcall
- ;; would be sufficient, but that is incorrect; it's possible for
- ;; a loop in the prompt body to be contified, and that loop need
- ;; not continue to the pop if it never terminates. The pop could
- ;; even be removed by DCE, in that case.
- (intset-any (lambda (succ)
- (or (not (intset-ref body succ))
- (<= succ label)))
- (intmap-ref succs label)))
- (intset-fold (lambda (pred succs)
- (intmap-replace succs pred handler intset-add))
- (if complete? body (intset-filter out-or-back-edge? body))
- succs)))
- (intmap-fold
- (lambda (label cont succs)
- (match cont
- (($ $kargs _ _
- ($ $continue _ _ ($ $prompt escape? tag handler)))
- (visit-prompt label handler succs))
- (_ succs)))
- conts
- succs))
-
-(define (rename-keys map old->new)
- (persistent-intmap
- (intmap-fold (lambda (k v out)
- (intmap-add! out (intmap-ref old->new k) v))
- map
- empty-intmap)))
-
-(define (rename-intset set old->new)
- (intset-fold (lambda (old set) (intset-add set (intmap-ref old->new old)))
- set empty-intset))
-
-(define (rename-graph graph old->new)
- (persistent-intmap
- (intmap-fold (lambda (pred succs out)
- (intmap-add! out
- (intmap-ref old->new pred)
- (rename-intset succs old->new)))
- graph
- empty-intmap)))
-
-(define (compute-live-variables cps defs uses)
- "Compute and return two values mapping LABEL->VAR..., where VAR... are
-the definitions that are live before and after LABEL, as intsets."
- (let* ((succs (add-prompt-control-flow-edges cps (compute-successors cps)))
- (preds (invert-graph succs))
- (old->new (compute-reverse-control-flow-order preds)))
- (call-with-values
- (lambda ()
- (let ((init (rename-keys
- (intmap-map (lambda (k v) empty-intset) preds)
- old->new)))
- (solve-flow-equations (rename-graph preds old->new)
- init init
- (rename-keys defs old->new)
- (rename-keys uses old->new)
- intset-subtract intset-union intset-union)))
- (lambda (in out)
- ;; As a reverse control-flow problem, the values flowing into a
- ;; node are actually the live values after the node executes.
- ;; Funny, innit? So we return them in the reverse order.
- (let ((new->old (invert-bijection old->new)))
- (values (rename-keys out new->old)
- (rename-keys in new->old)))))))
-
-(define (compute-needs-slot cps defs uses)
- (define (get-defs k) (intmap-ref defs k))
- (define (get-uses label) (intmap-ref uses label))
- (intmap-fold
- (lambda (label cont needs-slot)
- (intset-union
- needs-slot
- (match cont
- (($ $kargs _ _ ($ $continue k src exp))
- (let ((defs (get-defs label)))
- (define (defs+* uses)
- (intset-union defs uses))
- (define (defs+ use)
- (intset-add defs use))
- (match exp
- (($ $const)
- empty-intset)
- (($ $primcall 'free-ref (closure slot))
- (defs+ closure))
- (($ $primcall 'free-set! (closure slot value))
- (defs+* (intset closure value)))
- (($ $primcall 'cache-current-module! (mod . _))
- (defs+ mod))
- (($ $primcall 'cached-toplevel-box _)
- defs)
- (($ $primcall 'cached-module-box _)
- defs)
- (($ $primcall 'resolve (name bound?))
- (defs+ name))
- (($ $primcall 'make-vector/immediate (len init))
- (defs+ init))
- (($ $primcall 'vector-ref/immediate (v i))
- (defs+ v))
- (($ $primcall 'vector-set!/immediate (v i x))
- (defs+* (intset v x)))
- (($ $primcall 'allocate-struct/immediate (vtable nfields))
- (defs+ vtable))
- (($ $primcall 'struct-ref/immediate (s n))
- (defs+ s))
- (($ $primcall 'struct-set!/immediate (s n x))
- (defs+* (intset s x)))
- (($ $primcall 'builtin-ref (idx))
- defs)
- (_
- (defs+* (get-uses label))))))
- (($ $kreceive arity k)
- ;; Only allocate results of function calls to slots if they are
- ;; used.
- empty-intset)
- (($ $kclause arity body alternate)
- (get-defs label))
- (($ $kfun src meta self)
- (intset self))
- (($ $ktail)
- empty-intset))))
- cps
- empty-intset))
-
-(define (compute-lazy-vars cps live-in live-out defs needs-slot)
- "Compute and return a set of vars whose allocation can be delayed
-until their use is seen. These are \"lazy\" vars. A var is lazy if its
-uses are calls, it is always dead after the calls, and if the uses flow
-to the definition. A flow continues across a node iff the node kills no
-values that need slots, and defines only lazy vars. Calls also kill
-flows; there's no sense in trying to juggle a pending frame while there
-is an active call."
- (define (list->intset list)
- (persistent-intset
- (fold (lambda (i set) (intset-add! set i)) empty-intset list)))
-
- (let* ((succs (compute-successors cps))
- (gens (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs _ _ ($ $continue _ _ ($ $call proc args)))
- (intset-subtract (intset-add (list->intset args) proc)
- (intmap-ref live-out label)))
- (($ $kargs _ _ ($ $continue _ _ ($ $callk _ proc args)))
- (intset-subtract (intset-add (list->intset args) proc)
- (intmap-ref live-out label)))
- (_ #f)))
- cps))
- (kills (intmap-map
- (lambda (label in)
- (let* ((out (intmap-ref live-out label))
- (killed (intset-subtract in out))
- (killed-slots (intset-intersect killed needs-slot)))
- (and (eq? killed-slots empty-intset)
- ;; Kill output variables that need slots.
- (intset-intersect (intmap-ref defs label)
- needs-slot))))
- live-in))
- (preds (invert-graph succs))
- (old->new (compute-reverse-control-flow-order preds)))
- (define (subtract lazy kill)
- (cond
- ((eq? lazy empty-intset)
- lazy)
- ((not kill)
- empty-intset)
- ((and lazy (eq? empty-intset (intset-subtract kill lazy)))
- (intset-subtract lazy kill))
- (else
- empty-intset)))
- (define (add live gen) (or gen live))
- (define (meet in out)
- ;; Initial in is #f.
- (if in (intset-intersect in out) out))
- (call-with-values
- (lambda ()
- (let ((succs (rename-graph preds old->new))
- (in (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
- (out (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
- ;(out (rename-keys gens old->new))
- (kills (rename-keys kills old->new))
- (gens (rename-keys gens old->new)))
- (solve-flow-equations succs in out kills gens subtract add meet)))
- (lambda (in out)
- ;; A variable is lazy if its uses reach its definition.
- (intmap-fold (lambda (label out lazy)
- (match (intmap-ref cps label)
- (($ $kargs names vars)
- (let ((defs (list->intset vars)))
- (intset-union lazy (intset-intersect out defs))))
- (_ lazy)))
- (rename-keys out (invert-bijection old->new))
- empty-intset)))))
-
-(define (find-first-zero n)
- ;; Naive implementation.
- (let lp ((slot 0))
- (if (logbit? slot n)
- (lp (1+ slot))
- slot)))
-
-(define (find-first-trailing-zero n)
- (let lp ((slot (let lp ((count 2))
- (if (< n (ash 1 (1- count)))
- count
- ;; Grow upper bound slower than factor 2 to avoid
- ;; needless bignum allocation on 32-bit systems
- ;; when there are more than 16 locals.
- (lp (+ count (ash count -1)))))))
- (if (or (zero? slot) (logbit? (1- slot) n))
- slot
- (lp (1- slot)))))
-
-(define (integers from count)
- (if (zero? count)
- '()
- (cons from (integers (1+ from) (1- count)))))
-
-(define (solve-parallel-move src dst tmp)
- "Solve the parallel move problem between src and dst slot lists, which
-are comparable with eqv?. A tmp slot may be used."
-
- ;; This algorithm is taken from: "Tilting at windmills with Coq:
- ;; formal verification of a compilation algorithm for parallel moves"
- ;; by Laurence Rideau, Bernard Paul Serpette, and Xavier Leroy
- ;;
-
- (define (split-move moves reg)
- (let loop ((revhead '()) (tail moves))
- (match tail
- (((and s+d (s . d)) . rest)
- (if (eqv? s reg)
- (cons d (append-reverse revhead rest))
- (loop (cons s+d revhead) rest)))
- (_ #f))))
-
- (define (replace-last-source reg moves)
- (match moves
- ((moves ... (s . d))
- (append moves (list (cons reg d))))))
-
- (let loop ((to-move (map cons src dst))
- (being-moved '())
- (moved '())
- (last-source #f))
- ;; 'last-source' should always be equivalent to:
- ;; (and (pair? being-moved) (car (last being-moved)))
- (match being-moved
- (() (match to-move
- (() (reverse moved))
- (((and s+d (s . d)) . t1)
- (if (or (eqv? s d) ; idempotent
- (not s)) ; src is a constant and can be loaded directly
- (loop t1 '() moved #f)
- (loop t1 (list s+d) moved s)))))
- (((and s+d (s . d)) . b)
- (match (split-move to-move d)
- ((r . t1) (loop t1 (acons d r being-moved) moved last-source))
- (#f (match b
- (() (loop to-move '() (cons s+d moved) #f))
- (_ (if (eqv? d last-source)
- (loop to-move
- (replace-last-source tmp b)
- (cons s+d (acons d tmp moved))
- tmp)
- (loop to-move b (cons s+d moved) last-source))))))))))
-
-(define (compute-shuffles cps slots call-allocs live-in)
- (define (add-live-slot slot live-slots)
- (logior live-slots (ash 1 slot)))
-
- (define (get-cont label)
- (intmap-ref cps label))
-
- (define (get-slot var)
- (intmap-ref slots var (lambda (_) #f)))
-
- (define (get-slots vars)
- (let lp ((vars vars))
- (match vars
- ((var . vars) (cons (get-slot var) (lp vars)))
- (_ '()))))
-
- (define (get-proc-slot label)
- (call-alloc-proc-slot (intmap-ref call-allocs label)))
-
- (define (compute-live-slots label)
- (intset-fold (lambda (var live)
- (match (get-slot var)
- (#f live)
- (slot (add-live-slot slot live))))
- (intmap-ref live-in label)
- 0))
-
- ;; Although some parallel moves may proceed without a temporary slot,
- ;; in general one is needed. That temporary slot must not be part of
- ;; the source or destination sets, and that slot should not correspond
- ;; to a live variable. Usually the source and destination sets are a
- ;; subset of the union of the live sets before and after the move.
- ;; However for stack slots that don't have names -- those slots that
- ;; correspond to function arguments or to function return values -- it
- ;; could be that they are out of the computed live set. In that case
- ;; they need to be adjoined to the live set, used when choosing a
- ;; temporary slot.
- ;;
- ;; Note that although we reserve slots 253-255 for shuffling operands
- ;; that address less than the full 24-bit range of locals, that
- ;; reservation doesn't apply here, because this temporary itself is
- ;; used while doing parallel assignment via "mov", and "mov" does not
- ;; need shuffling.
- (define (compute-tmp-slot live stack-slots)
- (find-first-zero (fold add-live-slot live stack-slots)))
-
- (define (parallel-move src-slots dst-slots tmp-slot)
- (solve-parallel-move src-slots dst-slots tmp-slot))
-
- (define (compute-receive-shuffles label proc-slot)
- (match (get-cont label)
- (($ $kreceive arity kargs)
- (let* ((results (match (get-cont kargs)
- (($ $kargs names vars) vars)))
- (value-slots (integers (1+ proc-slot) (length results)))
- (result-slots (get-slots results))
- ;; Filter out unused results.
- (value-slots (filter-map (lambda (val result) (and result val))
- value-slots result-slots))
- (result-slots (filter (lambda (x) x) result-slots))
- (live (compute-live-slots kargs)))
- (parallel-move value-slots
- result-slots
- (compute-tmp-slot live value-slots))))))
-
- (define (add-call-shuffles label k args shuffles)
- (match (get-cont k)
- (($ $ktail)
- (let* ((live (compute-live-slots label))
- (tail-slots (integers 0 (length args)))
- (moves (parallel-move (get-slots args)
- tail-slots
- (compute-tmp-slot live tail-slots))))
- (intmap-add! shuffles label moves)))
- (($ $kreceive)
- (let* ((live (compute-live-slots label))
- (proc-slot (get-proc-slot label))
- (call-slots (integers proc-slot (length args)))
- (arg-moves (parallel-move (get-slots args)
- call-slots
- (compute-tmp-slot live call-slots))))
- (intmap-add! (intmap-add! shuffles label arg-moves)
- k (compute-receive-shuffles k proc-slot))))))
-
- (define (add-values-shuffles label k args shuffles)
- (match (get-cont k)
- (($ $ktail)
- (let* ((live (compute-live-slots label))
- (src-slots (get-slots args))
- (dst-slots (integers 1 (length args)))
- (moves (parallel-move src-slots dst-slots
- (compute-tmp-slot live dst-slots))))
- (intmap-add! shuffles label moves)))
- (($ $kargs _ dst-vars)
- (let* ((live (logior (compute-live-slots label)
- (compute-live-slots k)))
- (src-slots (get-slots args))
- (dst-slots (get-slots dst-vars))
- (moves (parallel-move src-slots dst-slots
- (compute-tmp-slot live '()))))
- (intmap-add! shuffles label moves)))))
-
- (define (add-prompt-shuffles label k handler shuffles)
- (intmap-add! shuffles handler
- (compute-receive-shuffles handler (get-proc-slot label))))
-
- (define (compute-shuffles label cont shuffles)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- (match exp
- (($ $call proc args)
- (add-call-shuffles label k (cons proc args) shuffles))
- (($ $callk _ proc args)
- (add-call-shuffles label k (cons proc args) shuffles))
- (($ $values args)
- (add-values-shuffles label k args shuffles))
- (($ $prompt escape? tag handler)
- (add-prompt-shuffles label k handler shuffles))
- (_ shuffles)))
- (_ shuffles)))
-
- (persistent-intmap
- (intmap-fold compute-shuffles cps empty-intmap)))
-
-(define (compute-frame-sizes cps slots call-allocs shuffles)
- ;; Minimum frame has one slot: the closure.
- (define minimum-frame-size 1)
- (define (get-shuffles label)
- (intmap-ref shuffles label))
- (define (get-proc-slot label)
- (match (intmap-ref call-allocs label (lambda (_) #f))
- (#f 0) ;; Tail call.
- (($ $call-alloc proc-slot) proc-slot)))
- (define (max-size var size)
- (match (intmap-ref slots var (lambda (_) #f))
- (#f size)
- (slot (max size (1+ slot)))))
- (define (max-size* vars size)
- (fold max-size size vars))
- (define (shuffle-size moves size)
- (match moves
- (() size)
- (((src . dst) . moves)
- (shuffle-size moves (max size (1+ src) (1+ dst))))))
- (define (call-size label nargs size)
- (shuffle-size (get-shuffles label)
- (max (+ (get-proc-slot label) nargs) size)))
- (define (measure-cont label cont frame-sizes clause size)
- (match cont
- (($ $kfun)
- (values #f #f #f))
- (($ $kclause)
- (let ((frame-sizes (if clause
- (intmap-add! frame-sizes clause size)
- empty-intmap)))
- (values frame-sizes label minimum-frame-size)))
- (($ $kargs names vars ($ $continue k src exp))
- (values frame-sizes clause
- (let ((size (max-size* vars size)))
- (match exp
- (($ $call proc args)
- (call-size label (1+ (length args)) size))
- (($ $callk _ proc args)
- (call-size label (1+ (length args)) size))
- (($ $values args)
- (shuffle-size (get-shuffles label) size))
- (_ size)))))
- (($ $kreceive)
- (values frame-sizes clause
- (shuffle-size (get-shuffles label) size)))
- (($ $ktail)
- (values (intmap-add! frame-sizes clause size) #f #f))))
-
- (persistent-intmap (intmap-fold measure-cont cps #f #f #f)))
-
-(define (allocate-args cps)
- (intmap-fold (lambda (label cont slots)
- (match cont
- (($ $kfun src meta self)
- (intmap-add! slots self 0))
- (($ $kclause arity body alt)
- (match (intmap-ref cps body)
- (($ $kargs names vars)
- (let lp ((vars vars) (slots slots) (n 1))
- (match vars
- (() slots)
- ((var . vars)
- (let ((n (if (<= 253 n 255) 256 n)))
- (lp vars
- (intmap-add! slots var n)
- (1+ n)))))))))
- (_ slots)))
- cps empty-intmap))
-
-(define-inlinable (add-live-slot slot live-slots)
- (logior live-slots (ash 1 slot)))
-
-(define-inlinable (kill-dead-slot slot live-slots)
- (logand live-slots (lognot (ash 1 slot))))
-
-(define-inlinable (compute-slot live-slots hint)
- ;; Slots 253-255 are reserved for shuffling; see comments in
- ;; assembler.scm.
- (if (and hint (not (logbit? hint live-slots))
- (or (< hint 253) (> hint 255)))
- hint
- (let ((slot (find-first-zero live-slots)))
- (if (or (< slot 253) (> slot 255))
- slot
- (+ 256 (find-first-zero (ash live-slots -256)))))))
-
-(define (allocate-lazy-vars cps slots call-allocs live-in lazy)
- (define (compute-live-slots slots label)
- (intset-fold (lambda (var live)
- (match (intmap-ref slots var (lambda (_) #f))
- (#f live)
- (slot (add-live-slot slot live))))
- (intmap-ref live-in label)
- 0))
-
- (define (allocate var hint slots live)
- (match (and hint (intmap-ref slots var (lambda (_) #f)))
- (#f (if (intset-ref lazy var)
- (let ((slot (compute-slot live hint)))
- (values (intmap-add! slots var slot)
- (add-live-slot slot live)))
- (values slots live)))
- (slot (values slots (add-live-slot slot live)))))
-
- (define (allocate* vars hints slots live)
- (match (vector vars hints)
- (#(() ()) slots)
- (#((var . vars) (hint . hints))
- (let-values (((slots live) (allocate var hint slots live)))
- (allocate* vars hints slots live)))))
-
- (define (get-proc-slot label)
- (match (intmap-ref call-allocs label (lambda (_) #f))
- (#f 0)
- (call (call-alloc-proc-slot call))))
-
- (define (allocate-call label args slots)
- (allocate* args (integers (get-proc-slot label) (length args))
- slots (compute-live-slots slots label)))
-
- (define (allocate-values label k args slots)
- (match (intmap-ref cps k)
- (($ $ktail)
- (allocate* args (integers 1 (length args))
- slots (compute-live-slots slots label)))
- (($ $kargs names vars)
- (allocate* args
- (map (cut intmap-ref slots <> (lambda (_) #f)) vars)
- slots (compute-live-slots slots label)))))
-
- (define (allocate-lazy label cont slots)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- (match exp
- (($ $call proc args)
- (allocate-call label (cons proc args) slots))
- (($ $callk _ proc args)
- (allocate-call label (cons proc args) slots))
- (($ $values args)
- (allocate-values label k args slots))
- (_ slots)))
- (_
- slots)))
-
- ;; Sweep right to left to visit uses before definitions.
- (persistent-intmap
- (intmap-fold-right allocate-lazy cps slots)))
-
-(define (allocate-slots cps)
- (let*-values (((defs uses) (compute-defs-and-uses cps))
- ((live-in live-out) (compute-live-variables cps defs uses))
- ((constants) (compute-constant-values cps))
- ((needs-slot) (compute-needs-slot cps defs uses))
- ((lazy) (compute-lazy-vars cps live-in live-out defs
- needs-slot)))
-
- (define (empty-live-slots)
- #b0)
-
- (define (compute-call-proc-slot live-slots)
- (+ 2 (find-first-trailing-zero live-slots)))
-
- (define (compute-prompt-handler-proc-slot live-slots)
- (if (zero? live-slots)
- 0
- (1- (find-first-trailing-zero live-slots))))
-
- (define (get-cont label)
- (intmap-ref cps label))
-
- (define (get-slot slots var)
- (intmap-ref slots var (lambda (_) #f)))
-
- (define (get-slots slots vars)
- (let lp ((vars vars))
- (match vars
- ((var . vars) (cons (get-slot slots var) (lp vars)))
- (_ '()))))
-
- (define (compute-live-slots* slots label live-vars)
- (intset-fold (lambda (var live)
- (match (get-slot slots var)
- (#f live)
- (slot (add-live-slot slot live))))
- (intmap-ref live-vars label)
- 0))
-
- (define (compute-live-in-slots slots label)
- (compute-live-slots* slots label live-in))
-
- (define (compute-live-out-slots slots label)
- (compute-live-slots* slots label live-out))
-
- (define (allocate var hint slots live)
- (cond
- ((not (intset-ref needs-slot var))
- (values slots live))
- ((get-slot slots var)
- => (lambda (slot)
- (values slots (add-live-slot slot live))))
- ((and (not hint) (intset-ref lazy var))
- (values slots live))
- (else
- (let ((slot (compute-slot live hint)))
- (values (intmap-add! slots var slot)
- (add-live-slot slot live))))))
-
- (define (allocate* vars hints slots live)
- (match (vector vars hints)
- (#(() ()) (values slots live))
- (#((var . vars) (hint . hints))
- (call-with-values (lambda () (allocate var hint slots live))
- (lambda (slots live)
- (allocate* vars hints slots live))))))
-
- (define (allocate-defs label vars slots)
- (let ((live (compute-live-in-slots slots label))
- (live-vars (intmap-ref live-in label)))
- (let lp ((vars vars) (slots slots) (live live))
- (match vars
- (() (values slots live))
- ((var . vars)
- (call-with-values (lambda () (allocate var #f slots live))
- (lambda (slots live)
- (lp vars slots
- (let ((slot (get-slot slots var)))
- (if (and slot (not (intset-ref live-vars var)))
- (kill-dead-slot slot live)
- live))))))))))
-
- ;; PRE-LIVE are the live slots coming into the term. POST-LIVE
- ;; is the subset of PRE-LIVE that is still live after the term
- ;; uses its inputs.
- (define (allocate-call label k args slots call-allocs pre-live)
- (match (get-cont k)
- (($ $ktail)
- (let ((tail-slots (integers 0 (length args))))
- (values (allocate* args tail-slots slots pre-live)
- call-allocs)))
- (($ $kreceive arity kargs)
- (let*-values
- (((post-live) (compute-live-out-slots slots label))
- ((proc-slot) (compute-call-proc-slot post-live))
- ((call-slots) (integers proc-slot (length args)))
- ((slots pre-live) (allocate* args call-slots slots pre-live))
- ;; Allow the first result to be hinted by its use, but
- ;; hint the remaining results to stay in place. This
- ;; strikes a balance between avoiding shuffling,
- ;; especially for unused extra values, and avoiding frame
- ;; size growth due to sparse locals.
- ((slots result-live)
- (match (get-cont kargs)
- (($ $kargs () ())
- (values slots post-live))
- (($ $kargs (_ . _) (_ . results))
- (let ((result-slots (integers (+ proc-slot 2)
- (length results))))
- (allocate* results result-slots slots post-live)))))
- ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
- (lognot post-live)))
- ((call) (make-call-alloc proc-slot dead-slot-map)))
- (values slots
- (intmap-add! call-allocs label call))))))
-
- (define (allocate-values label k args slots call-allocs)
- (match (get-cont k)
- (($ $ktail)
- (values slots call-allocs))
- (($ $kargs (_) (dst))
- ;; When there is only one value in play, we allow the dst to be
- ;; hinted (see compute-lazy-vars). If the src doesn't have a
- ;; slot, then the actual slot for the dst would end up being
- ;; decided by the call that args it. Because we don't know the
- ;; slot, we can't really compute the parallel moves in that
- ;; case, so just bail and rely on the bytecode emitter to
- ;; handle the one-value case specially.
- (match args
- ((src)
- (let ((post-live (compute-live-out-slots slots label)))
- (values (allocate dst (get-slot slots src) slots post-live)
- call-allocs)))))
- (($ $kargs _ dst-vars)
- (let ((src-slots (get-slots slots args))
- (post-live (compute-live-out-slots slots label)))
- (values (allocate* dst-vars src-slots slots post-live)
- call-allocs)))))
-
- (define (allocate-prompt label k handler slots call-allocs)
- (match (get-cont handler)
- (($ $kreceive arity kargs)
- (let*-values
- (((handler-live) (compute-live-in-slots slots handler))
- ((proc-slot) (compute-prompt-handler-proc-slot handler-live))
- ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
- (lognot handler-live)))
- ((result-vars) (match (get-cont kargs)
- (($ $kargs names vars) vars)))
- ((value-slots) (integers (1+ proc-slot) (length result-vars)))
- ((slots result-live) (allocate* result-vars value-slots
- slots handler-live)))
- (values slots
- (intmap-add! call-allocs label
- (make-call-alloc proc-slot dead-slot-map)))))))
-
- (define (allocate-cont label cont slots call-allocs)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- (let-values (((slots live) (allocate-defs label vars slots)))
- (match exp
- (($ $call proc args)
- (allocate-call label k (cons proc args) slots call-allocs live))
- (($ $callk _ proc args)
- (allocate-call label k (cons proc args) slots call-allocs live))
- (($ $values args)
- (allocate-values label k args slots call-allocs))
- (($ $prompt escape? tag handler)
- (allocate-prompt label k handler slots call-allocs))
- (_
- (values slots call-allocs)))))
- (_
- (values slots call-allocs))))
-
- (call-with-values (lambda ()
- (let ((slots (allocate-args cps)))
- (intmap-fold allocate-cont cps slots empty-intmap)))
- (lambda (slots calls)
- (let* ((slots (allocate-lazy-vars cps slots calls live-in lazy))
- (shuffles (compute-shuffles cps slots calls live-in))
- (frame-sizes (compute-frame-sizes cps slots calls shuffles)))
- (make-allocation slots constants calls shuffles frame-sizes))))))
diff --git a/module/language/cps2/spec.scm b/module/language/cps2/spec.scm
deleted file mode 100644
index ac8f06439..000000000
--- a/module/language/cps2/spec.scm
+++ /dev/null
@@ -1,37 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2015 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
-
-;;; Code:
-
-(define-module (language cps2 spec)
- #:use-module (system base language)
- #:use-module (language cps2)
- #:use-module (language cps2 compile-bytecode)
- #:export (cps2))
-
-(define* (write-cps exp #:optional (port (current-output-port)))
- (write (unparse-cps exp) port))
-
-(define-language cps2
- #:title "CPS2 Intermediate Language"
- #:reader (lambda (port env) (read port))
- #:printer write-cps
- #:parser parse-cps
- #:compilers `((bytecode . ,compile-bytecode))
- #:for-humans? #f
- )
diff --git a/module/language/cps2/specialize-primcalls.scm b/module/language/cps2/specialize-primcalls.scm
deleted file mode 100644
index 00d2149d7..000000000
--- a/module/language/cps2/specialize-primcalls.scm
+++ /dev/null
@@ -1,59 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Some bytecode operations can encode an immediate as an operand.
-;;; This pass tranforms generic primcalls to these specialized
-;;; primcalls, if possible.
-;;;
-;;; Code:
-
-(define-module (language cps2 specialize-primcalls)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:export (specialize-primcalls))
-
-(define (specialize-primcalls conts)
- (let ((constants (compute-constant-values conts)))
- (define (immediate-u8? var)
- (let ((val (intmap-ref constants var (lambda (_) #f))))
- (and (exact-integer? val) (<= 0 val 255))))
- (define (specialize-primcall name args)
- (match (cons name args)
- (('make-vector (? immediate-u8? n) init) 'make-vector/immediate)
- (('vector-ref v (? immediate-u8? n)) 'vector-ref/immediate)
- (('vector-set! v (? immediate-u8? n) x) 'vector-set!/immediate)
- (('allocate-struct v (? immediate-u8? n)) 'allocate-struct/immediate)
- (('struct-ref s (? immediate-u8? n)) 'struct-ref/immediate)
- (('struct-set! s (? immediate-u8? n) x) 'struct-set!/immediate)
- (_ #f)))
- (intmap-map
- (lambda (label cont)
- (match cont
- (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
- (let ((name* (specialize-primcall name args)))
- (if name*
- (build-cont
- ($kargs names vars
- ($continue k src ($primcall name* args))))
- cont)))
- (_ cont)))
- conts)))
diff --git a/module/language/cps2/split-rec.scm b/module/language/cps2/split-rec.scm
deleted file mode 100644
index aeb1c6397..000000000
--- a/module/language/cps2/split-rec.scm
+++ /dev/null
@@ -1,174 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Split functions bound in $rec expressions into strongly-connected
-;;; components. The result will be that each $rec binds a
-;;; strongly-connected component of mutually recursive functions.
-;;;
-;;; Code:
-
-(define-module (language cps2 split-rec)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold))
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (split-rec))
-
-(define (compute-free-vars conts kfun)
- "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
-references."
- (define (add-def var defs) (intset-add! defs var))
- (define (add-defs vars defs)
- (match vars
- (() defs)
- ((var . vars) (add-defs vars (add-def var defs)))))
- (define (add-use var uses) (intset-add! uses var))
- (define (add-uses vars uses)
- (match vars
- (() uses)
- ((var . vars) (add-uses vars (add-use var uses)))))
- (define (visit-nested-funs body)
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _
- ($ $fun kfun)))
- (intmap-union out (visit-fun kfun)))
- (($ $kargs _ _ ($ $continue _ _
- ($ $rec _ _ (($ $fun kfun) ...))))
- (fold (lambda (kfun out)
- (intmap-union out (visit-fun kfun)))
- out kfun))
- (_ out)))
- body
- empty-intmap))
- (define (visit-fun kfun)
- (let* ((body (compute-function-body conts kfun))
- (free (visit-nested-funs body)))
- (call-with-values
- (lambda ()
- (intset-fold
- (lambda (label defs uses)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (values
- (add-defs vars defs)
- (match exp
- ((or ($ $const) ($ $prim)) uses)
- (($ $fun kfun)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- (($ $rec names vars (($ $fun kfun) ...))
- (fold (lambda (kfun uses)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- uses kfun))
- (($ $values args)
- (add-uses args uses))
- (($ $call proc args)
- (add-use proc (add-uses args uses)))
- (($ $branch kt ($ $values (arg)))
- (add-use arg uses))
- (($ $branch kt ($ $primcall name args))
- (add-uses args uses))
- (($ $primcall name args)
- (add-uses args uses))
- (($ $prompt escape? tag handler)
- (add-use tag uses)))))
- (($ $kfun src meta self)
- (values (add-def self defs) uses))
- (_ (values defs uses))))
- body empty-intset empty-intset))
- (lambda (defs uses)
- (intmap-add free kfun (intset-subtract
- (persistent-intset uses)
- (persistent-intset defs)))))))
- (visit-fun kfun))
-
-(define (compute-split fns free-vars)
- (define (get-free kfun)
- ;; It's possible for a fun to have been skipped by
- ;; compute-free-vars, if the fun isn't reachable. Fall back to
- ;; empty-intset for the fun's free vars, in that case.
- (intmap-ref free-vars kfun (lambda (_) empty-intset)))
- (let* ((vars (intmap-keys fns))
- (edges (intmap-map
- (lambda (var kfun)
- (intset-intersect (get-free kfun) vars))
- fns)))
- (compute-sorted-strongly-connected-components edges)))
-
-(define (intmap-acons k v map)
- (intmap-add map k v))
-
-(define (split-rec conts)
- (let ((free (compute-free-vars conts 0)))
- (with-fresh-name-state conts
- (persistent-intmap
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs cont-names cont-vars
- ($ $continue k src ($ $rec names vars (($ $fun kfuns) ...))))
- (let ((fns (fold intmap-acons empty-intmap vars kfuns))
- (fn-names (fold intmap-acons empty-intmap vars names)))
- (match (compute-split fns free)
- (()
- ;; Remove trivial $rec.
- (with-cps out
- (setk label ($kargs cont-names cont-vars
- ($continue k src ($values ()))))))
- ((_)
- ;; Bound functions already form a strongly-connected
- ;; component.
- out)
- (components
- ;; Multiple components. Split them into separate $rec
- ;; expressions.
- (define (build-body out components)
- (match components
- (()
- (match (intmap-ref out k)
- (($ $kargs names vars term)
- (with-cps (intmap-remove out k)
- term))))
- ((vars . components)
- (match (intset-fold
- (lambda (var out)
- (let ((name (intmap-ref fn-names var))
- (fun (build-exp
- ($fun (intmap-ref fns var)))))
- (cons (list name var fun) out)))
- vars '())
- (((name var fun) ...)
- (with-cps out
- (let$ body (build-body components))
- (letk kbody ($kargs name var ,body))
- (build-term
- ($continue kbody src ($rec name var fun)))))))))
- (with-cps out
- (let$ body (build-body components))
- (setk label ($kargs cont-names cont-vars ,body)))))))
- (_ out)))
- conts
- conts)))))
diff --git a/module/language/cps2/type-fold.scm b/module/language/cps2/type-fold.scm
deleted file mode 100644
index d1bc1aaa3..000000000
--- a/module/language/cps2/type-fold.scm
+++ /dev/null
@@ -1,425 +0,0 @@
-;;; Abstract constant folding on CPS
-;;; Copyright (C) 2014, 2015 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 program. If not, see
-;;; .
-
-;;; Commentary:
-;;;
-;;; This pass uses the abstract interpretation provided by type analysis
-;;; to fold constant values and type predicates. It is most profitably
-;;; run after CSE, to take advantage of scalar replacement.
-;;;
-;;; Code:
-
-(define-module (language cps2 type-fold)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 renumber)
- #:use-module (language cps2 types)
- #:use-module (language cps2 with-cps)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:use-module (system base target)
- #:export (type-fold))
-
-
-
-
-;; Branch folders.
-
-(define &scalar-types
- (logior &exact-integer &flonum &char &unspecified &false &true &nil &null))
-
-(define *branch-folders* (make-hash-table))
-
-(define-syntax-rule (define-branch-folder name f)
- (hashq-set! *branch-folders* 'name f))
-
-(define-syntax-rule (define-branch-folder-alias to from)
- (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from)))
-
-(define-syntax-rule (define-unary-branch-folder (name arg min max) body ...)
- (define-branch-folder name (lambda (arg min max) body ...)))
-
-(define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0
- arg1 min1 max1)
- body ...)
- (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...)))
-
-(define-syntax-rule (define-unary-type-predicate-folder name &type)
- (define-unary-branch-folder (name type min max)
- (let ((type* (logand type &type)))
- (cond
- ((zero? type*) (values #t #f))
- ((eqv? type type*) (values #t #t))
- (else (values #f #f))))))
-
-;; All the cases that are in compile-bytecode.
-(define-unary-type-predicate-folder pair? &pair)
-(define-unary-type-predicate-folder null? &null)
-(define-unary-type-predicate-folder nil? &nil)
-(define-unary-type-predicate-folder symbol? &symbol)
-(define-unary-type-predicate-folder variable? &box)
-(define-unary-type-predicate-folder vector? &vector)
-(define-unary-type-predicate-folder struct? &struct)
-(define-unary-type-predicate-folder string? &string)
-(define-unary-type-predicate-folder number? &number)
-(define-unary-type-predicate-folder char? &char)
-
-(define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1)
- (cond
- ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0))
- (values #t #f))
- ((and (eqv? type0 type1)
- (eqv? min0 min1 max0 max1)
- (zero? (logand type0 (1- type0)))
- (not (zero? (logand type0 &scalar-types))))
- (values #t #t))
- (else
- (values #f #f))))
-(define-branch-folder-alias eqv? eq?)
-(define-branch-folder-alias equal? eq?)
-
-(define (compare-ranges type0 min0 max0 type1 min1 max1)
- (and (zero? (logand (logior type0 type1) (lognot &real)))
- (cond ((< max0 min1) '<)
- ((> min0 max1) '>)
- ((= min0 max0 min1 max1) '=)
- ((<= max0 min1) '<=)
- ((>= min0 max1) '>=)
- (else #f))))
-
-(define-binary-branch-folder (< type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((<) (values #t #t))
- ((= >= >) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((< <= =) (values #t #t))
- ((>) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((=) (values #t #t))
- ((< >) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((> >= =) (values #t #t))
- ((<) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (> type0 min0 max0 type1 min1 max1)
- (case (compare-ranges type0 min0 max0 type1 min1 max1)
- ((>) (values #t #t))
- ((= <= <) (values #t #f))
- (else (values #f #f))))
-
-(define-binary-branch-folder (logtest type0 min0 max0 type1 min1 max1)
- (define (logand-min a b)
- (if (< a b 0)
- (min a b)
- 0))
- (define (logand-max a b)
- (if (< a b 0)
- 0
- (max a b)))
- (if (and (= min0 max0) (= min1 max1) (eqv? type0 type1 &exact-integer))
- (values #t (logtest min0 min1))
- (values #f #f)))
-
-
-
-
-;; Strength reduction.
-
-(define *primcall-reducers* (make-hash-table))
-
-(define-syntax-rule (define-primcall-reducer name f)
- (hashq-set! *primcall-reducers* 'name f))
-
-(define-syntax-rule (define-unary-primcall-reducer (name cps k src
- arg type min max)
- body ...)
- (define-primcall-reducer name
- (lambda (cps k src arg type min max)
- body ...)))
-
-(define-syntax-rule (define-binary-primcall-reducer (name cps k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- body ...)
- (define-primcall-reducer name
- (lambda (cps k src arg0 type0 min0 max0 arg1 type1 min1 max1)
- body ...)))
-
-(define-binary-primcall-reducer (mul cps k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- (define (fail) (with-cps cps #f))
- (define (negate arg)
- (with-cps cps
- ($ (with-cps-constants ((zero 0))
- (build-term
- ($continue k src ($primcall 'sub (zero arg))))))))
- (define (zero)
- (with-cps cps
- (build-term ($continue k src ($const 0)))))
- (define (identity arg)
- (with-cps cps
- (build-term ($continue k src ($values (arg))))))
- (define (double arg)
- (with-cps cps
- (build-term ($continue k src ($primcall 'add (arg arg))))))
- (define (power-of-two constant arg)
- (let ((n (let lp ((bits 0) (constant constant))
- (if (= constant 1) bits (lp (1+ bits) (ash constant -1))))))
- (with-cps cps
- ($ (with-cps-constants ((bits n))
- (build-term ($continue k src ($primcall 'ash (arg bits)))))))))
- (define (mul/constant constant constant-type arg arg-type)
- (cond
- ((not (or (= constant-type &exact-integer) (= constant-type arg-type)))
- (fail))
- ((eqv? constant -1)
- ;; (* arg -1) -> (- 0 arg)
- (negate arg))
- ((eqv? constant 0)
- ;; (* arg 0) -> 0 if arg is not a flonum or complex
- (and (= constant-type &exact-integer)
- (zero? (logand arg-type
- (lognot (logior &flonum &complex))))
- (zero)))
- ((eqv? constant 1)
- ;; (* arg 1) -> arg
- (identity arg))
- ((eqv? constant 2)
- ;; (* arg 2) -> (+ arg arg)
- (double arg))
- ((and (= constant-type arg-type &exact-integer)
- (positive? constant)
- (zero? (logand constant (1- constant))))
- ;; (* arg power-of-2) -> (ash arg (log2 power-of-2
- (power-of-two constant arg))
- (else
- (fail))))
- (cond
- ((logtest (logior type0 type1) (lognot &number)) (fail))
- ((= min0 max0) (mul/constant min0 type0 arg1 type1))
- ((= min1 max1) (mul/constant min1 type1 arg0 type0))
- (else (fail))))
-
-(define-binary-primcall-reducer (logbit? cps k src
- arg0 type0 min0 max0
- arg1 type1 min1 max1)
- (define (convert-to-logtest cps kbool)
- (define (compute-mask cps kmask src)
- (if (eq? min0 max0)
- (with-cps cps
- (build-term
- ($continue kmask src ($const (ash 1 min0)))))
- (with-cps cps
- ($ (with-cps-constants ((one 1))
- (build-term
- ($continue kmask src ($primcall 'ash (one arg0)))))))))
- (with-cps cps
- (letv mask)
- (letk kt ($kargs () ()
- ($continue kbool src ($const #t))))
- (letk kf ($kargs () ()
- ($continue kbool src ($const #f))))
- (letk kmask ($kargs (#f) (mask)
- ($continue kf src
- ($branch kt ($primcall 'logtest (mask arg1))))))
- ($ (compute-mask kmask src))))
- ;; Hairiness because we are converting from a primcall with unknown
- ;; arity to a branching primcall.
- (let ((positive-fixnum-bits (- (* (target-word-size) 8) 3)))
- (if (and (= type0 &exact-integer)
- (<= 0 min0 positive-fixnum-bits)
- (<= 0 max0 positive-fixnum-bits))
- (match (intmap-ref cps k)
- (($ $kreceive arity kargs)
- (match arity
- (($ $arity (_) () (not #f) () #f)
- (with-cps cps
- (letv bool)
- (let$ body (with-cps-constants ((nil '()))
- (build-term
- ($continue kargs src ($values (bool nil))))))
- (letk kbool ($kargs (#f) (bool) ,body))
- ($ (convert-to-logtest kbool))))
- (_
- (with-cps cps
- (letv bool)
- (letk kbool ($kargs (#f) (bool)
- ($continue k src ($primcall 'values (bool)))))
- ($ (convert-to-logtest kbool))))))
- (($ $ktail)
- (with-cps cps
- (letv bool)
- (letk kbool ($kargs (#f) (bool)
- ($continue k src ($primcall 'return (bool)))))
- ($ (convert-to-logtest kbool)))))
- (with-cps cps #f))))
-
-
-
-
-;;
-
-(define (local-type-fold start end cps)
- (define (scalar-value type val)
- (cond
- ((eqv? type &exact-integer) val)
- ((eqv? type &flonum) (exact->inexact val))
- ((eqv? type &char) (integer->char val))
- ((eqv? type &unspecified) *unspecified*)
- ((eqv? type &false) #f)
- ((eqv? type &true) #t)
- ((eqv? type &nil) #nil)
- ((eqv? type &null) '())
- (else (error "unhandled type" type val))))
- (let ((types (infer-types cps start)))
- (define (fold-primcall cps label names vars k src name args def)
- (call-with-values (lambda () (lookup-post-type types label def 0))
- (lambda (type min max)
- (and (not (zero? type))
- (zero? (logand type (1- type)))
- (zero? (logand type (lognot &scalar-types)))
- (eqv? min max)
- (let ((val (scalar-value type min)))
- ;; (pk 'folded src name args val)
- (with-cps cps
- (letv v*)
- (letk k* ($kargs (#f) (v*)
- ($continue k src ($const val))))
- ;; Rely on DCE to elide this expression, if
- ;; possible.
- (setk label
- ($kargs names vars
- ($continue k* src ($primcall name args))))))))))
- (define (reduce-primcall cps label names vars k src name args)
- (and=>
- (hashq-ref *primcall-reducers* name)
- (lambda (reducer)
- (match args
- ((arg0)
- (call-with-values (lambda () (lookup-pre-type types label arg0))
- (lambda (type0 min0 max0)
- (call-with-values (lambda ()
- (reducer cps k src arg0 type0 min0 max0))
- (lambda (cps term)
- (and term
- (with-cps cps
- (setk label ($kargs names vars ,term)))))))))
- ((arg0 arg1)
- (call-with-values (lambda () (lookup-pre-type types label arg0))
- (lambda (type0 min0 max0)
- (call-with-values (lambda () (lookup-pre-type types label arg1))
- (lambda (type1 min1 max1)
- (call-with-values (lambda ()
- (reducer cps k src arg0 type0 min0 max0
- arg1 type1 min1 max1))
- (lambda (cps term)
- (and term
- (with-cps cps
- (setk label ($kargs names vars ,term)))))))))))
- (_ #f)))))
- (define (fold-unary-branch cps label names vars kf kt src name arg)
- (and=>
- (hashq-ref *branch-folders* name)
- (lambda (folder)
- (call-with-values (lambda () (lookup-pre-type types label arg))
- (lambda (type min max)
- (call-with-values (lambda () (folder type min max))
- (lambda (f? v)
- ;; (when f? (pk 'folded-unary-branch label name arg v))
- (and f?
- (with-cps cps
- (setk label
- ($kargs names vars
- ($continue (if v kt kf) src
- ($values ())))))))))))))
- (define (fold-binary-branch cps label names vars kf kt src name arg0 arg1)
- (and=>
- (hashq-ref *branch-folders* name)
- (lambda (folder)
- (call-with-values (lambda () (lookup-pre-type types label arg0))
- (lambda (type0 min0 max0)
- (call-with-values (lambda () (lookup-pre-type types label arg1))
- (lambda (type1 min1 max1)
- (call-with-values (lambda ()
- (folder type0 min0 max0 type1 min1 max1))
- (lambda (f? v)
- ;; (when f? (pk 'folded-binary-branch label name arg0 arg1 v))
- (and f?
- (with-cps cps
- (setk label
- ($kargs names vars
- ($continue (if v kt kf) src
- ($values ())))))))))))))))
- (define (visit-expression cps label names vars k src exp)
- (match exp
- (($ $primcall name args)
- ;; We might be able to fold primcalls that define a value.
- (match (intmap-ref cps k)
- (($ $kargs (_) (def))
- (or (fold-primcall cps label names vars k src name args def)
- (reduce-primcall cps label names vars k src name args)
- cps))
- (_
- (or (reduce-primcall cps label names vars k src name args)
- cps))))
- (($ $branch kt ($ $primcall name args))
- ;; We might be able to fold primcalls that branch.
- (match args
- ((x)
- (or (fold-unary-branch cps label names vars k kt src name x)
- cps))
- ((x y)
- (or (fold-binary-branch cps label names vars k kt src name x y)
- cps))))
- (_ cps)))
- (let lp ((label start) (cps cps))
- (if (<= label end)
- (lp (1+ label)
- (match (intmap-ref cps label)
- (($ $kargs names vars ($ $continue k src exp))
- (visit-expression cps label names vars k src exp))
- (_ cps)))
- cps))))
-
-(define (fold-functions-in-renumbered-program f conts seed)
- (let* ((conts (persistent-intmap conts))
- (end (1+ (intmap-prev conts))))
- (let lp ((label 0) (seed seed))
- (if (eqv? label end)
- seed
- (match (intmap-ref conts label)
- (($ $kfun src meta self tail clause)
- (lp (1+ tail) (f label tail seed))))))))
-
-(define (type-fold conts)
- ;; Type analysis wants a program whose labels are sorted.
- (let ((conts (renumber conts)))
- (with-fresh-name-state conts
- (persistent-intmap
- (fold-functions-in-renumbered-program local-type-fold conts conts)))))
diff --git a/module/language/cps2/types.scm b/module/language/cps2/types.scm
deleted file mode 100644
index 07da3d6a0..000000000
--- a/module/language/cps2/types.scm
+++ /dev/null
@@ -1,1408 +0,0 @@
-;;; Type analysis on CPS
-;;; Copyright (C) 2014, 2015 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 program. If not, see
-;;; .
-
-;;; Commentary:
-;;;
-;;; Type analysis computes the possible types and ranges that values may
-;;; have at all program positions. This analysis can help to prove that
-;;; a primcall has no side-effects, if its arguments have the
-;;; appropriate type and range. It can also enable constant folding of
-;;; type predicates and, in the future, enable the compiler to choose
-;;; untagged, unboxed representations for numbers.
-;;;
-;;; For the purposes of this analysis, a "type" is an aspect of a value
-;;; that will not change. Guile's CPS intermediate language does not
-;;; carry manifest type information that asserts properties about given
-;;; values; instead, we recover this information via flow analysis,
-;;; garnering properties from type predicates, constant literals,
-;;; primcall results, and primcalls that assert that their arguments are
-;;; of particular types.
-;;;
-;;; A range denotes a subset of the set of values in a type, bounded by
-;;; a minimum and a maximum. The precise meaning of a range depends on
-;;; the type. For real numbers, the range indicates an inclusive lower
-;;; and upper bound on the integer value of a type. For vectors, the
-;;; range indicates the length of the vector. The range is limited to a
-;;; signed 32-bit value, with the smallest and largest values indicating
-;;; -inf.0 and +inf.0, respectively. For some types, like pairs, the
-;;; concept of "range" makes no sense. In these cases we consider the
-;;; range to be -inf.0 to +inf.0.
-;;;
-;;; Types are represented as a bitfield. Fewer bits means a more precise
-;;; type. Although normally only values that have a single type will
-;;; have an associated range, this is not enforced. The range applies
-;;; to all types in the bitfield. When control flow meets, the types and
-;;; ranges meet with the union operator.
-;;;
-;;; It is not practical to precisely compute value ranges in all cases.
-;;; For example, in the following case:
-;;;
-;;; (let lp ((n 0)) (when (foo) (lp (1+ n))))
-;;;
-;;; The first time that range analysis visits the program, N is
-;;; determined to be the exact integer 0. The second time, it is an
-;;; exact integer in the range [0, 1]; the third, [0, 2]; and so on.
-;;; This analysis will terminate, but only after the positive half of
-;;; the 32-bit range has been fully explored and we decide that the
-;;; range of N is [0, +inf.0]. At the same time, we want to do range
-;;; analysis and type analysis at the same time, as there are
-;;; interactions between them, notably in the case of `sqrt' which
-;;; returns a complex number if its argument cannot be proven to be
-;;; non-negative. So what we do instead is to precisely propagate types
-;;; and ranges when propagating forward, but after the first backwards
-;;; branch is seen, we cause backward branches that would expand the
-;;; range of a value to saturate that range towards positive or negative
-;;; infinity (as appropriate).
-;;;
-;;; A naive approach to type analysis would build up a table that has
-;;; entries for all variables at all program points, but this has
-;;; N-squared complexity and quickly grows unmanageable. Instead, we
-;;; use _intmaps_ from (language cps intmap) to share state between
-;;; connected program points.
-;;;
-;;; Code:
-
-(define-module (language cps2 types)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:use-module (rnrs bytevectors)
- #:use-module (srfi srfi-11)
- #:export (;; Specific types.
- &exact-integer
- &flonum
- &complex
- &fraction
-
- &char
- &unspecified
- &unbound
- &false
- &true
- &nil
- &null
- &symbol
- &keyword
-
- &procedure
-
- &pointer
- &fluid
- &pair
- &vector
- &box
- &struct
- &string
- &bytevector
- &bitvector
- &array
- &hash-table
-
- ;; Union types.
- &number &real
-
- infer-types
- lookup-pre-type
- lookup-post-type
- primcall-types-check?))
-
-(define-syntax define-flags
- (lambda (x)
- (syntax-case x ()
- ((_ all shift name ...)
- (let ((count (length #'(name ...))))
- (with-syntax (((n ...) (iota count))
- (count count))
- #'(begin
- (define-syntax name (identifier-syntax (ash 1 n)))
- ...
- (define-syntax all (identifier-syntax (1- (ash 1 count))))
- (define-syntax shift (identifier-syntax count)))))))))
-
-;; More precise types have fewer bits.
-(define-flags &all-types &type-bits
- &exact-integer
- &flonum
- &complex
- &fraction
-
- &char
- &unspecified
- &unbound
- &false
- &true
- &nil
- &null
- &symbol
- &keyword
-
- &procedure
-
- &pointer
- &fluid
- &pair
- &vector
- &box
- &struct
- &string
- &bytevector
- &bitvector
- &array
- &hash-table)
-
-(define-syntax &no-type (identifier-syntax 0))
-
-(define-syntax &number
- (identifier-syntax (logior &exact-integer &flonum &complex &fraction)))
-(define-syntax &real
- (identifier-syntax (logior &exact-integer &flonum &fraction)))
-
-(define-syntax *max-s32* (identifier-syntax (- (ash 1 31) 1)))
-(define-syntax *min-s32* (identifier-syntax (- 0 (ash 1 31))))
-
-;; Versions of min and max that do not coerce exact numbers to become
-;; inexact.
-(define min
- (case-lambda
- ((a b) (if (< a b) a b))
- ((a b c) (min (min a b) c))
- ((a b c d) (min (min a b) c d))))
-(define max
- (case-lambda
- ((a b) (if (> a b) a b))
- ((a b c) (max (max a b) c))
- ((a b c d) (max (max a b) c d))))
-
-
-
-(define-syntax-rule (define-compile-time-value name val)
- (define-syntax name
- (make-variable-transformer
- (lambda (x)
- (syntax-case x (set!)
- (var (identifier? #'var)
- (datum->syntax #'var val)))))))
-
-(define-compile-time-value min-fixnum most-negative-fixnum)
-(define-compile-time-value max-fixnum most-positive-fixnum)
-
-(define-inlinable (make-unclamped-type-entry type min max)
- (vector type min max))
-(define-inlinable (type-entry-type tentry)
- (vector-ref tentry 0))
-(define-inlinable (type-entry-clamped-min tentry)
- (vector-ref tentry 1))
-(define-inlinable (type-entry-clamped-max tentry)
- (vector-ref tentry 2))
-
-(define-syntax-rule (clamp-range val)
- (cond
- ((< val min-fixnum) min-fixnum)
- ((< max-fixnum val) max-fixnum)
- (else val)))
-
-(define-inlinable (make-type-entry type min max)
- (vector type (clamp-range min) (clamp-range max)))
-(define-inlinable (type-entry-min tentry)
- (let ((min (type-entry-clamped-min tentry)))
- (if (eq? min min-fixnum) -inf.0 min)))
-(define-inlinable (type-entry-max tentry)
- (let ((max (type-entry-clamped-max tentry)))
- (if (eq? max max-fixnum) +inf.0 max)))
-
-(define all-types-entry (make-type-entry &all-types -inf.0 +inf.0))
-
-(define* (var-type-entry typeset var #:optional (default all-types-entry))
- (intmap-ref typeset var (lambda (_) default)))
-
-(define (var-type typeset var)
- (type-entry-type (var-type-entry typeset var)))
-(define (var-min typeset var)
- (type-entry-min (var-type-entry typeset var)))
-(define (var-max typeset var)
- (type-entry-max (var-type-entry typeset var)))
-
-;; Is the type entry A contained entirely within B?
-(define (type-entry<=? a b)
- (match (cons a b)
- ((#(a-type a-min a-max) . #(b-type b-min b-max))
- (and (eqv? b-type (logior a-type b-type))
- (<= b-min a-min)
- (>= b-max a-max)))))
-
-(define (type-entry-union a b)
- (cond
- ((type-entry<=? b a) a)
- ((type-entry<=? a b) b)
- (else (make-type-entry
- (logior (type-entry-type a) (type-entry-type b))
- (min (type-entry-clamped-min a) (type-entry-clamped-min b))
- (max (type-entry-clamped-max a) (type-entry-clamped-max b))))))
-
-(define (type-entry-saturating-union a b)
- (cond
- ((type-entry<=? b a) a)
- (else
- (make-type-entry
- (logior (type-entry-type a) (type-entry-type b))
- (let ((a-min (type-entry-clamped-min a))
- (b-min (type-entry-clamped-min b)))
- (if (< b-min a-min) min-fixnum a-min))
- (let ((a-max (type-entry-clamped-max a))
- (b-max (type-entry-clamped-max b)))
- (if (> b-max a-max) max-fixnum a-max))))))
-
-(define (type-entry-intersection a b)
- (cond
- ((type-entry<=? a b) a)
- ((type-entry<=? b a) b)
- (else (make-type-entry
- (logand (type-entry-type a) (type-entry-type b))
- (max (type-entry-clamped-min a) (type-entry-clamped-min b))
- (min (type-entry-clamped-max a) (type-entry-clamped-max b))))))
-
-(define (adjoin-var typeset var entry)
- (intmap-add typeset var entry type-entry-union))
-
-(define (restrict-var typeset var entry)
- (intmap-add typeset var entry type-entry-intersection))
-
-(define (constant-type val)
- "Compute the type and range of VAL. Return three values: the type,
-minimum, and maximum."
- (define (return type val)
- (if val
- (make-type-entry type val val)
- (make-type-entry type -inf.0 +inf.0)))
- (cond
- ((number? val)
- (cond
- ((exact-integer? val) (return &exact-integer val))
- ((eqv? (imag-part val) 0)
- (if (nan? val)
- (make-type-entry &flonum -inf.0 +inf.0)
- (make-type-entry
- (if (exact? val) &fraction &flonum)
- (if (rational? val) (inexact->exact (floor val)) val)
- (if (rational? val) (inexact->exact (ceiling val)) val))))
- (else (return &complex #f))))
- ((eq? val '()) (return &null #f))
- ((eq? val #nil) (return &nil #f))
- ((eq? val #t) (return &true #f))
- ((eq? val #f) (return &false #f))
- ((char? val) (return &char (char->integer val)))
- ((eqv? val *unspecified*) (return &unspecified #f))
- ((symbol? val) (return &symbol #f))
- ((keyword? val) (return &keyword #f))
- ((pair? val) (return &pair #f))
- ((vector? val) (return &vector (vector-length val)))
- ((string? val) (return &string (string-length val)))
- ((bytevector? val) (return &bytevector (bytevector-length val)))
- ((bitvector? val) (return &bitvector (bitvector-length val)))
- ((array? val) (return &array (array-rank val)))
- ((not (variable-bound? (make-variable val))) (return &unbound #f))
-
- (else (error "unhandled constant" val))))
-
-(define *type-checkers* (make-hash-table))
-(define *type-inferrers* (make-hash-table))
-
-(define-syntax-rule (define-type-helper name)
- (define-syntax-parameter name
- (lambda (stx)
- (syntax-violation 'name
- "macro used outside of define-type"
- stx))))
-(define-type-helper define!)
-(define-type-helper restrict!)
-(define-type-helper &type)
-(define-type-helper &min)
-(define-type-helper &max)
-
-(define-syntax-rule (define-type-checker (name arg ...) body ...)
- (hashq-set!
- *type-checkers*
- 'name
- (lambda (typeset arg ...)
- (syntax-parameterize
- ((&type (syntax-rules () ((_ val) (var-type typeset val))))
- (&min (syntax-rules () ((_ val) (var-min typeset val))))
- (&max (syntax-rules () ((_ val) (var-max typeset val)))))
- body ...))))
-
-(define-syntax-rule (check-type arg type min max)
- ;; If the arg is negative, it is a closure variable.
- (and (>= arg 0)
- (zero? (logand (lognot type) (&type arg)))
- (<= min (&min arg))
- (<= (&max arg) max)))
-
-(define-syntax-rule (define-type-inferrer* (name succ var ...) body ...)
- (hashq-set!
- *type-inferrers*
- 'name
- (lambda (in succ var ...)
- (let ((out in))
- (syntax-parameterize
- ((define!
- (syntax-rules ()
- ((_ val type min max)
- (set! out (adjoin-var out val
- (make-type-entry type min max))))))
- (restrict!
- (syntax-rules ()
- ((_ val type min max)
- (set! out (restrict-var out val
- (make-type-entry type min max))))))
- (&type (syntax-rules () ((_ val) (var-type in val))))
- (&min (syntax-rules () ((_ val) (var-min in val))))
- (&max (syntax-rules () ((_ val) (var-max in val)))))
- body ...
- out)))))
-
-(define-syntax-rule (define-type-inferrer (name arg ...) body ...)
- (define-type-inferrer* (name succ arg ...) body ...))
-
-(define-syntax-rule (define-predicate-inferrer (name arg ... true?) body ...)
- (define-type-inferrer* (name succ arg ...)
- (let ((true? (not (zero? succ))))
- body ...)))
-
-(define-syntax define-simple-type-checker
- (lambda (x)
- (define (parse-spec l)
- (syntax-case l ()
- (() '())
- (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
- (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
- ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
- (syntax-case x ()
- ((_ (name arg-spec ...) result-spec ...)
- (with-syntax
- (((arg ...) (generate-temporaries #'(arg-spec ...)))
- (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...))))
- #'(define-type-checker (name arg ...)
- (and (check-type arg arg-type arg-min arg-max)
- ...)))))))
-
-(define-syntax define-simple-type-inferrer
- (lambda (x)
- (define (parse-spec l)
- (syntax-case l ()
- (() '())
- (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
- (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
- ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
- (syntax-case x ()
- ((_ (name arg-spec ...) result-spec ...)
- (with-syntax
- (((arg ...) (generate-temporaries #'(arg-spec ...)))
- (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...)))
- ((res ...) (generate-temporaries #'(result-spec ...)))
- (((res-type res-min res-max) ...) (parse-spec #'(result-spec ...))))
- #'(define-type-inferrer (name arg ... res ...)
- (restrict! arg arg-type arg-min arg-max)
- ...
- (define! res res-type res-min res-max)
- ...))))))
-
-(define-syntax-rule (define-simple-type (name arg-spec ...) result-spec ...)
- (begin
- (define-simple-type-checker (name arg-spec ...))
- (define-simple-type-inferrer (name arg-spec ...) result-spec ...)))
-
-(define-syntax-rule (define-simple-types
- ((name arg-spec ...) result-spec ...)
- ...)
- (begin
- (define-simple-type (name arg-spec ...) result-spec ...)
- ...))
-
-(define-syntax-rule (define-type-checker-aliases orig alias ...)
- (let ((check (hashq-ref *type-checkers* 'orig)))
- (hashq-set! *type-checkers* 'alias check)
- ...))
-(define-syntax-rule (define-type-inferrer-aliases orig alias ...)
- (let ((check (hashq-ref *type-inferrers* 'orig)))
- (hashq-set! *type-inferrers* 'alias check)
- ...))
-(define-syntax-rule (define-type-aliases orig alias ...)
- (begin
- (define-type-checker-aliases orig alias ...)
- (define-type-inferrer-aliases orig alias ...)))
-
-
-
-
-;;; This list of primcall type definitions follows the order of
-;;; effects-analysis.scm; please keep it in a similar order.
-;;;
-;;; There is no need to add checker definitions for expressions that do
-;;; not exhibit the &type-check effect, as callers should not ask if
-;;; such an expression does or does not type-check. For those that do
-;;; exhibit &type-check, you should define a type inferrer unless the
-;;; primcall will never typecheck.
-;;;
-;;; Likewise there is no need to define inferrers for primcalls which
-;;; return &all-types values and which never raise exceptions from which
-;;; we can infer the types of incoming values.
-
-
-
-
-;;;
-;;; Generic effect-free predicates.
-;;;
-
-(define-predicate-inferrer (eq? a b true?)
- ;; We can only propagate information down the true leg.
- (when true?
- (let ((type (logand (&type a) (&type b)))
- (min (max (&min a) (&min b)))
- (max (min (&max a) (&max b))))
- (restrict! a type min max)
- (restrict! b type min max))))
-(define-type-inferrer-aliases eq? eqv? equal?)
-
-(define-syntax-rule (define-simple-predicate-inferrer predicate type)
- (define-predicate-inferrer (predicate val true?)
- (let ((type (if true?
- type
- (logand (&type val) (lognot type)))))
- (restrict! val type -inf.0 +inf.0))))
-(define-simple-predicate-inferrer pair? &pair)
-(define-simple-predicate-inferrer null? &null)
-(define-simple-predicate-inferrer nil? &nil)
-(define-simple-predicate-inferrer symbol? &symbol)
-(define-simple-predicate-inferrer variable? &box)
-(define-simple-predicate-inferrer vector? &vector)
-(define-simple-predicate-inferrer struct? &struct)
-(define-simple-predicate-inferrer string? &string)
-(define-simple-predicate-inferrer bytevector? &bytevector)
-(define-simple-predicate-inferrer bitvector? &bitvector)
-(define-simple-predicate-inferrer keyword? &keyword)
-(define-simple-predicate-inferrer number? &number)
-(define-simple-predicate-inferrer char? &char)
-(define-simple-predicate-inferrer procedure? &procedure)
-(define-simple-predicate-inferrer thunk? &procedure)
-
-
-
-;;;
-;;; Fluids. Note that we can't track bound-ness of fluids, as pop-fluid
-;;; can change boundness.
-;;;
-
-(define-simple-types
- ((fluid-ref (&fluid 1)) &all-types)
- ((fluid-set! (&fluid 0 1) &all-types))
- ((push-fluid (&fluid 0 1) &all-types))
- ((pop-fluid)))
-
-
-
-
-;;;
-;;; Prompts. (Nothing to do.)
-;;;
-
-
-
-
-;;;
-;;; Pairs.
-;;;
-
-(define-simple-types
- ((cons &all-types &all-types) &pair)
- ((car &pair) &all-types)
- ((set-car! &pair &all-types))
- ((cdr &pair) &all-types)
- ((set-cdr! &pair &all-types)))
-
-
-
-
-;;;
-;;; Variables.
-;;;
-
-(define-simple-types
- ((box &all-types) (&box 1))
- ((box-ref (&box 1)) &all-types))
-
-(define-simple-type-checker (box-set! (&box 0 1) &all-types))
-(define-type-inferrer (box-set! box val)
- (restrict! box &box 1 1))
-
-
-
-
-;;;
-;;; Vectors.
-;;;
-
-;; This max-vector-len computation is a hack.
-(define *max-vector-len* (ash most-positive-fixnum -5))
-
-(define-simple-type-checker (make-vector (&exact-integer 0 *max-vector-len*)
- &all-types))
-(define-type-inferrer (make-vector size init result)
- (restrict! size &exact-integer 0 *max-vector-len*)
- (define! result &vector (max (&min size) 0) (&max size)))
-
-(define-type-checker (vector-ref v idx)
- (and (check-type v &vector 0 *max-vector-len*)
- (check-type idx &exact-integer 0 (1- (&min v)))))
-(define-type-inferrer (vector-ref v idx result)
- (restrict! v &vector (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max v)))
- (define! result &all-types -inf.0 +inf.0))
-
-(define-type-checker (vector-set! v idx val)
- (and (check-type v &vector 0 *max-vector-len*)
- (check-type idx &exact-integer 0 (1- (&min v)))))
-(define-type-inferrer (vector-set! v idx val)
- (restrict! v &vector (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max v))))
-
-(define-type-aliases make-vector make-vector/immediate)
-(define-type-aliases vector-ref vector-ref/immediate)
-(define-type-aliases vector-set! vector-set!/immediate)
-
-(define-simple-type-checker (vector-length &vector))
-(define-type-inferrer (vector-length v result)
- (restrict! v &vector 0 *max-vector-len*)
- (define! result &exact-integer (max (&min v) 0)
- (min (&max v) *max-vector-len*)))
-
-
-
-
-;;;
-;;; Structs.
-;;;
-
-;; No type-checker for allocate-struct, as we can't currently check that
-;; vt is actually a vtable.
-(define-type-inferrer (allocate-struct vt size result)
- (restrict! vt &struct vtable-offset-user +inf.0)
- (restrict! size &exact-integer 0 +inf.0)
- (define! result &struct (max (&min size) 0) (&max size)))
-
-(define-type-checker (struct-ref s idx)
- (and (check-type s &struct 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- ;; FIXME: is the field readable?
- (< (&max idx) (&min s))))
-(define-type-inferrer (struct-ref s idx result)
- (restrict! s &struct (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (define! result &all-types -inf.0 +inf.0))
-
-(define-type-checker (struct-set! s idx val)
- (and (check-type s &struct 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- ;; FIXME: is the field writable?
- (< (&max idx) (&min s))))
-(define-type-inferrer (struct-set! s idx val)
- (restrict! s &struct (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s))))
-
-(define-type-aliases allocate-struct allocate-struct/immediate)
-(define-type-aliases struct-ref struct-ref/immediate)
-(define-type-aliases struct-set! struct-set!/immediate)
-
-(define-simple-type (struct-vtable (&struct 0 +inf.0))
- (&struct vtable-offset-user +inf.0))
-
-
-
-
-;;;
-;;; Strings.
-;;;
-
-(define *max-char* (1- (ash 1 24)))
-
-(define-type-checker (string-ref s idx)
- (and (check-type s &string 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (< (&max idx) (&min s))))
-(define-type-inferrer (string-ref s idx result)
- (restrict! s &string (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (define! result &char 0 *max-char*))
-
-(define-type-checker (string-set! s idx val)
- (and (check-type s &string 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (check-type val &char 0 *max-char*)
- (< (&max idx) (&min s))))
-(define-type-inferrer (string-set! s idx val)
- (restrict! s &string (1+ (&min idx)) +inf.0)
- (restrict! idx &exact-integer 0 (1- (&max s)))
- (restrict! val &char 0 *max-char*))
-
-(define-simple-type-checker (string-length &string))
-(define-type-inferrer (string-length s result)
- (restrict! s &string 0 +inf.0)
- (define! result &exact-integer (max (&min s) 0) (&max s)))
-
-(define-simple-type (number->string &number) (&string 0 +inf.0))
-(define-simple-type (string->number (&string 0 +inf.0))
- ((logior &number &false) -inf.0 +inf.0))
-
-
-
-
-;;;
-;;; Bytevectors.
-;;;
-
-(define-simple-type-checker (bytevector-length &bytevector))
-(define-type-inferrer (bytevector-length bv result)
- (restrict! bv &bytevector 0 +inf.0)
- (define! result &exact-integer (max (&min bv) 0) (&max bv)))
-
-(define-syntax-rule (define-bytevector-accessors ref set type size min max)
- (begin
- (define-type-checker (ref bv idx)
- (and (check-type bv &bytevector 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (< (&max idx) (- (&min bv) size))))
- (define-type-inferrer (ref bv idx result)
- (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
- (restrict! idx &exact-integer 0 (- (&max bv) size))
- (define! result type min max))
- (define-type-checker (set bv idx val)
- (and (check-type bv &bytevector 0 +inf.0)
- (check-type idx &exact-integer 0 +inf.0)
- (check-type val type min max)
- (< (&max idx) (- (&min bv) size))))
- (define-type-inferrer (set! bv idx val)
- (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
- (restrict! idx &exact-integer 0 (- (&max bv) size))
- (restrict! val type min max))))
-
-(define-syntax-rule (define-short-bytevector-accessors ref set size signed?)
- (define-bytevector-accessors ref set &exact-integer size
- (if signed? (- (ash 1 (1- (* size 8)))) 0)
- (1- (ash 1 (if signed? (1- (* size 8)) (* size 8))))))
-
-(define-short-bytevector-accessors bv-u8-ref bv-u8-set! 1 #f)
-(define-short-bytevector-accessors bv-s8-ref bv-s8-set! 1 #t)
-(define-short-bytevector-accessors bv-u16-ref bv-u16-set! 2 #f)
-(define-short-bytevector-accessors bv-s16-ref bv-s16-set! 2 #t)
-
-;; The range analysis only works on signed 32-bit values, so some limits
-;; are out of range.
-(define-bytevector-accessors bv-u32-ref bv-u32-set! &exact-integer 4 0 +inf.0)
-(define-bytevector-accessors bv-s32-ref bv-s32-set! &exact-integer 4 -inf.0 +inf.0)
-(define-bytevector-accessors bv-u64-ref bv-u64-set! &exact-integer 8 0 +inf.0)
-(define-bytevector-accessors bv-s64-ref bv-s64-set! &exact-integer 8 -inf.0 +inf.0)
-(define-bytevector-accessors bv-f32-ref bv-f32-set! &real 4 -inf.0 +inf.0)
-(define-bytevector-accessors bv-f64-ref bv-f64-set! &real 8 -inf.0 +inf.0)
-
-
-
-
-;;;
-;;; Numbers.
-;;;
-
-;; First, branching primitives with no results.
-(define-simple-type-checker (= &number &number))
-(define-predicate-inferrer (= a b true?)
- (when (and true?
- (zero? (logand (logior (&type a) (&type b)) (lognot &number))))
- (let ((min (max (&min a) (&min b)))
- (max (min (&max a) (&max b))))
- (restrict! a &number min max)
- (restrict! b &number min max))))
-
-(define (restricted-comparison-ranges op type0 min0 max0 type1 min1 max1)
- (define (infer-integer-ranges)
- (match op
- ('< (values min0 (min max0 (1- max1)) (max (1+ min0) min1) max1))
- ('<= (values min0 (min max0 max1) (max min0 min1) max1))
- ('>= (values (max min0 min1) max0 min1 (min max0 max1)))
- ('> (values (max min0 (1+ min1)) max0 min1 (min (1- max0) max1)))))
- (define (infer-real-ranges)
- (match op
- ((or '< '<=) (values min0 (min max0 max1) (max min0 min1) max1))
- ((or '> '>=) (values (max min0 min1) max0 min1 (min max0 max1)))))
- (if (= (logior type0 type1) &exact-integer)
- (infer-integer-ranges)
- (infer-real-ranges)))
-
-(define-syntax-rule (define-comparison-inferrer (op inverse))
- (define-predicate-inferrer (op a b true?)
- (when (zero? (logand (logior (&type a) (&type b)) (lognot &number)))
- (call-with-values
- (lambda ()
- (restricted-comparison-ranges (if true? 'op 'inverse)
- (&type a) (&min a) (&max a)
- (&type b) (&min b) (&max b)))
- (lambda (min0 max0 min1 max1)
- (restrict! a &real min0 max0)
- (restrict! b &real min1 max1))))))
-
-(define-simple-type-checker (< &real &real))
-(define-comparison-inferrer (< >=))
-
-(define-simple-type-checker (<= &real &real))
-(define-comparison-inferrer (<= >))
-
-(define-simple-type-checker (>= &real &real))
-(define-comparison-inferrer (>= <))
-
-(define-simple-type-checker (> &real &real))
-(define-comparison-inferrer (> <=))
-
-;; Arithmetic.
-(define-syntax-rule (define-unary-result! a result min max)
- (let ((min* min)
- (max* max)
- (type (logand (&type a) &number)))
- (cond
- ((not (= type (&type a)))
- ;; Not a number. Punt and do nothing.
- (define! result &all-types -inf.0 +inf.0))
- ;; Complex numbers don't have a range.
- ((eqv? type &complex)
- (define! result &complex -inf.0 +inf.0))
- (else
- (define! result type min* max*)))))
-
-(define-syntax-rule (define-binary-result! a b result closed? min max)
- (let ((min* min)
- (max* max)
- (a-type (logand (&type a) &number))
- (b-type (logand (&type b) &number)))
- (cond
- ((or (not (= a-type (&type a))) (not (= b-type (&type b))))
- ;; One input not a number. Perhaps we end up dispatching to
- ;; GOOPS.
- (define! result &all-types -inf.0 +inf.0))
- ;; Complex and floating-point numbers are contagious.
- ((or (eqv? a-type &complex) (eqv? b-type &complex))
- (define! result &complex -inf.0 +inf.0))
- ((or (eqv? a-type &flonum) (eqv? b-type &flonum))
- (define! result &flonum min* max*))
- ;; Exact integers are closed under some operations.
- ((and closed? (eqv? a-type &exact-integer) (eqv? b-type &exact-integer))
- (define! result &exact-integer min* max*))
- (else
- ;; Fractions may become integers.
- (let ((type (logior a-type b-type)))
- (define! result
- (if (zero? (logand type &fraction))
- type
- (logior type &exact-integer))
- min* max*))))))
-
-(define-simple-type-checker (add &number &number))
-(define-type-inferrer (add a b result)
- (define-binary-result! a b result #t
- (+ (&min a) (&min b))
- (+ (&max a) (&max b))))
-
-(define-simple-type-checker (sub &number &number))
-(define-type-inferrer (sub a b result)
- (define-binary-result! a b result #t
- (- (&min a) (&max b))
- (- (&max a) (&min b))))
-
-(define-simple-type-checker (mul &number &number))
-(define-type-inferrer (mul a b result)
- (let ((min-a (&min a)) (max-a (&max a))
- (min-b (&min b)) (max-b (&max b))
- ;; We only really get +inf.0 at runtime for flonums and
- ;; compnums. If we have inferred that the arguments are not
- ;; flonums and not compnums, then the result of (* +inf.0 0) at
- ;; range inference time is 0 and not +nan.0.
- (nan-impossible? (not (logtest (logior (&type a) (&type b))
- (logior &flonum &complex)))))
- (define (nan* a b)
- (if (and (or (and (inf? a) (zero? b))
- (and (zero? a) (inf? b)))
- nan-impossible?)
- 0
- (* a b)))
- (let ((-- (nan* min-a min-b))
- (-+ (nan* min-a max-b))
- (++ (nan* max-a max-b))
- (+- (nan* max-a min-b)))
- (let ((has-nan? (or (nan? --) (nan? -+) (nan? ++) (nan? +-))))
- (define-binary-result! a b result #t
- (cond
- ((eqv? a b) 0)
- (has-nan? -inf.0)
- (else (min -- -+ ++ +-)))
- (if has-nan?
- +inf.0
- (max -- -+ ++ +-)))))))
-
-(define-type-checker (div a b)
- (and (check-type a &number -inf.0 +inf.0)
- (check-type b &number -inf.0 +inf.0)
- ;; We only know that there will not be an exception if b is not
- ;; zero.
- (not (<= (&min b) 0 (&max b)))))
-(define-type-inferrer (div a b result)
- (let ((min-a (&min a)) (max-a (&max a))
- (min-b (&min b)) (max-b (&max b)))
- (call-with-values
- (lambda ()
- (if (<= min-b 0 max-b)
- ;; If the range of the divisor crosses 0, the result spans
- ;; the whole range.
- (values -inf.0 +inf.0)
- ;; Otherwise min-b and max-b have the same sign, and cannot both
- ;; be infinity.
- (let ((--- (if (inf? min-b) 0 (floor/ min-a min-b)))
- (-+- (if (inf? max-b) 0 (floor/ min-a max-b)))
- (++- (if (inf? max-b) 0 (floor/ max-a max-b)))
- (+-- (if (inf? min-b) 0 (floor/ max-a min-b)))
- (--+ (if (inf? min-b) 0 (ceiling/ min-a min-b)))
- (-++ (if (inf? max-b) 0 (ceiling/ min-a max-b)))
- (+++ (if (inf? max-b) 0 (ceiling/ max-a max-b)))
- (+-+ (if (inf? min-b) 0 (ceiling/ max-a min-b))))
- (values (min (min --- -+- ++- +--)
- (min --+ -++ +++ +-+))
- (max (max --- -+- ++- +--)
- (max --+ -++ +++ +-+))))))
- (lambda (min max)
- (define-binary-result! a b result #f min max)))))
-
-(define-simple-type-checker (add1 &number))
-(define-type-inferrer (add1 a result)
- (define-unary-result! a result (1+ (&min a)) (1+ (&max a))))
-
-(define-simple-type-checker (sub1 &number))
-(define-type-inferrer (sub1 a result)
- (define-unary-result! a result (1- (&min a)) (1- (&max a))))
-
-(define-type-checker (quo a b)
- (and (check-type a &exact-integer -inf.0 +inf.0)
- (check-type b &exact-integer -inf.0 +inf.0)
- ;; We only know that there will not be an exception if b is not
- ;; zero.
- (not (<= (&min b) 0 (&max b)))))
-(define-type-inferrer (quo a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer -inf.0 +inf.0))
-
-(define-type-checker-aliases quo rem)
-(define-type-inferrer (rem a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- ;; Same sign as A.
- (let ((max-abs-rem (1- (max (abs (&min b)) (abs (&max b))))))
- (cond
- ((< (&min a) 0)
- (if (< 0 (&max a))
- (define! result &exact-integer (- max-abs-rem) max-abs-rem)
- (define! result &exact-integer (- max-abs-rem) 0)))
- (else
- (define! result &exact-integer 0 max-abs-rem)))))
-
-(define-type-checker-aliases quo mod)
-(define-type-inferrer (mod a b result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- ;; Same sign as B.
- (let ((max-abs-mod (1- (max (abs (&min b)) (abs (&max b))))))
- (cond
- ((< (&min b) 0)
- (if (< 0 (&max b))
- (define! result &exact-integer (- max-abs-mod) max-abs-mod)
- (define! result &exact-integer (- max-abs-mod) 0)))
- (else
- (define! result &exact-integer 0 max-abs-mod)))))
-
-;; Predicates.
-(define-syntax-rule (define-number-kind-predicate-inferrer name type)
- (define-type-inferrer (name val result)
- (cond
- ((zero? (logand (&type val) type))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (lognot type)))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0)))))
-(define-number-kind-predicate-inferrer complex? &number)
-(define-number-kind-predicate-inferrer real? &real)
-(define-number-kind-predicate-inferrer rational?
- (logior &exact-integer &fraction))
-(define-number-kind-predicate-inferrer integer?
- (logior &exact-integer &flonum))
-(define-number-kind-predicate-inferrer exact-integer?
- &exact-integer)
-
-(define-simple-type-checker (exact? &number))
-(define-type-inferrer (exact? val result)
- (restrict! val &number -inf.0 +inf.0)
- (cond
- ((zero? (logand (&type val) (logior &exact-integer &fraction)))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (lognot (logior &exact-integer &fraction))))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-simple-type-checker (inexact? &number))
-(define-type-inferrer (inexact? val result)
- (restrict! val &number -inf.0 +inf.0)
- (cond
- ((zero? (logand (&type val) (logior &flonum &complex)))
- (define! result &false 0 0))
- ((zero? (logand (&type val) (logand &number
- (lognot (logior &flonum &complex)))))
- (define! result &true 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-simple-type-checker (inf? &real))
-(define-type-inferrer (inf? val result)
- (restrict! val &real -inf.0 +inf.0)
- (cond
- ((or (zero? (logand (&type val) (logior &flonum &complex)))
- (and (not (inf? (&min val))) (not (inf? (&max val)))))
- (define! result &false 0 0))
- (else
- (define! result (logior &true &false) 0 0))))
-
-(define-type-aliases inf? nan?)
-
-(define-simple-type (even? &exact-integer)
- ((logior &true &false) 0 0))
-(define-type-aliases even? odd?)
-
-;; Bit operations.
-(define-simple-type-checker (ash &exact-integer &exact-integer))
-(define-type-inferrer (ash val count result)
- (define (ash* val count)
- ;; As we can only represent a 32-bit range, don't bother inferring
- ;; shifts that might exceed that range.
- (cond
- ((inf? val) val) ; Preserves sign.
- ((< -32 count 32) (ash val count))
- ((zero? val) 0)
- ((positive? val) +inf.0)
- (else -inf.0)))
- (restrict! val &exact-integer -inf.0 +inf.0)
- (restrict! count &exact-integer -inf.0 +inf.0)
- (let ((-- (ash* (&min val) (&min count)))
- (-+ (ash* (&min val) (&max count)))
- (++ (ash* (&max val) (&max count)))
- (+- (ash* (&max val) (&min count))))
- (define! result &exact-integer
- (min -- -+ ++ +-)
- (max -- -+ ++ +-))))
-
-(define (next-power-of-two n)
- (let lp ((out 1))
- (if (< n out)
- out
- (lp (ash out 1)))))
-
-(define-simple-type-checker (logand &exact-integer &exact-integer))
-(define-type-inferrer (logand a b result)
- (define (logand-min a b)
- (if (and (negative? a) (negative? b))
- (min a b)
- 0))
- (define (logand-max a b)
- (if (and (positive? a) (positive? b))
- (min a b)
- 0))
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (logand-min (&min a) (&min b))
- (logand-max (&max a) (&max b))))
-
-(define-simple-type-checker (logior &exact-integer &exact-integer))
-(define-type-inferrer (logior a b result)
- ;; Saturate all bits of val.
- (define (saturate val)
- (1- (next-power-of-two val)))
- (define (logior-min a b)
- (cond ((and (< a 0) (<= 0 b)) a)
- ((and (< b 0) (<= 0 a)) b)
- (else (max a b))))
- (define (logior-max a b)
- ;; If either operand is negative, just assume the max is -1.
- (cond
- ((or (< a 0) (< b 0)) -1)
- ((or (inf? a) (inf? b)) +inf.0)
- (else (saturate (logior a b)))))
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (logior-min (&min a) (&min b))
- (logior-max (&max a) (&max b))))
-
-;; For our purposes, treat logxor the same as logior.
-(define-type-aliases logior logxor)
-
-(define-simple-type-checker (lognot &exact-integer))
-(define-type-inferrer (lognot a result)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (define! result &exact-integer
- (- -1 (&max a))
- (- -1 (&min a))))
-
-(define-simple-type-checker (logtest &exact-integer &exact-integer))
-(define-predicate-inferrer (logtest a b true?)
- (restrict! a &exact-integer -inf.0 +inf.0)
- (restrict! b &exact-integer -inf.0 +inf.0))
-
-(define-simple-type-checker (logbit? (&exact-integer 0 +inf.0) &exact-integer))
-(define-type-inferrer (logbit? a b result)
- (let ((a-min (&min a))
- (a-max (&max a))
- (b-min (&min b))
- (b-max (&max b)))
- (if (and (eqv? a-min a-max) (>= a-min 0) (not (inf? a-min))
- (eqv? b-min b-max) (>= b-min 0) (not (inf? b-min)))
- (let ((type (if (logbit? a-min b-min) &true &false)))
- (define! result type 0 0))
- (define! result (logior &true &false) 0 0))))
-
-;; Flonums.
-(define-simple-type-checker (sqrt &number))
-(define-type-inferrer (sqrt x result)
- (let ((type (&type x)))
- (cond
- ((and (zero? (logand type &complex)) (<= 0 (&min x)))
- (define! result
- (logior type &flonum)
- (inexact->exact (floor (sqrt (&min x))))
- (if (inf? (&max x))
- +inf.0
- (inexact->exact (ceiling (sqrt (&max x)))))))
- (else
- (define! result (logior type &flonum &complex) -inf.0 +inf.0)))))
-
-(define-simple-type-checker (abs &real))
-(define-type-inferrer (abs x result)
- (let ((type (&type x)))
- (cond
- ((eqv? type (logand type &number))
- (restrict! x &real -inf.0 +inf.0)
- (define! result (logand type &real)
- (min (abs (&min x)) (abs (&max x)))
- (max (abs (&min x)) (abs (&max x)))))
- (else
- (define! result (logior (logand (&type x) (lognot &number))
- (logand (&type x) &real))
- (max (&min x) 0)
- (max (abs (&min x)) (abs (&max x))))))))
-
-
-
-
-;;;
-;;; Characters.
-;;;
-
-(define-simple-type (char &char &char)
- ((logior &true &false) 0 0))
-(define-type-aliases char char<=? char>=? char>?)
-
-(define-simple-type-checker (integer->char (&exact-integer 0 #x10ffff)))
-(define-type-inferrer (integer->char i result)
- (restrict! i &exact-integer 0 #x10ffff)
- (define! result &char (max (&min i) 0) (min (&max i) #x10ffff)))
-
-(define-simple-type-checker (char->integer &char))
-(define-type-inferrer (char->integer c result)
- (restrict! c &char 0 #x10ffff)
- (define! result &exact-integer (max (&min c) 0) (min (&max c) #x10ffff)))
-
-
-
-
-;;;
-;;; Type flow analysis: the meet (ahem) of the algorithm.
-;;;
-
-(define (successor-count cont)
- (match cont
- (($ $kargs _ _ ($ $continue k src exp))
- (match exp
- ((or ($ $branch) ($ $prompt)) 2)
- (_ 1)))
- (($ $kfun src meta self tail clause) (if clause 1 0))
- (($ $kclause arity body alt) (if alt 2 1))
- (($ $kreceive) 1)
- (($ $ktail) 0)))
-
-(define (intset-pop set)
- (match (intset-next set)
- (#f (values set #f))
- (i (values (intset-remove set i) i))))
-
-(define-syntax-rule (make-worklist-folder* seed ...)
- (lambda (f worklist seed ...)
- (let lp ((worklist worklist) (seed seed) ...)
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist i)
- (if i
- (call-with-values (lambda () (f i seed ...))
- (lambda (i* seed ...)
- (let add ((i* i*) (worklist worklist))
- (match i*
- (() (lp worklist seed ...))
- ((i . i*) (add i* (intset-add worklist i)))))))
- (values seed ...)))))))
-
-(define worklist-fold*
- (case-lambda
- ((f worklist seed)
- ((make-worklist-folder* seed) f worklist seed))))
-
-(define intmap-ensure
- (let* ((*absent* (list 'absent))
- (not-found (lambda (i) *absent*)))
- (lambda (map i ensure)
- (let ((val (intmap-ref map i not-found)))
- (if (eq? val *absent*)
- (let ((val (ensure i)))
- (values (intmap-add map i val) val))
- (values map val))))))
-
-;; For best results, the labels in the function starting should be
-;; topologically sorted (renumbered). Otherwise the backward branch
-;; detection mentioned in the module commentary will trigger for
-;; ordinary forward branches.
-(define (infer-types conts kfun)
- "Compute types for all variables bound in the function labelled
-@var{kfun}, from @var{conts}. Returns an intmap mapping labels to type
-entries.
-
-A type entry is a vector that describes the types of the values that
-flow into and out of a labelled expressoin. The first slot in the type
-entry vector corresponds to the types that flow in, and the rest of the
-slots correspond to the types that flow out. Each element of the type
-entry vector is an intmap mapping variable name to the variable's
-inferred type. An inferred type is a 3-vector of type, minimum, and
-maximum, where type is a bitset as a fixnum."
- (define (get-entry typev label) (intmap-ref typev label))
- (define (entry-not-found label)
- (make-vector (1+ (successor-count (intmap-ref conts label))) #f))
- (define (ensure-entry typev label)
- (intmap-ensure typev label entry-not-found))
-
- (define (compute-initial-state)
- (let ((entry (entry-not-found kfun)))
- ;; Nothing flows in to the first label.
- (vector-set! entry 0 empty-intmap)
- (intmap-add empty-intmap kfun entry)))
-
- (define (adjoin-vars types vars entry)
- (match vars
- (() types)
- ((var . vars)
- (adjoin-vars (adjoin-var types var entry) vars entry))))
-
- (define (infer-primcall types succ name args result)
- (cond
- ((hashq-ref *type-inferrers* name)
- => (lambda (inferrer)
- ;; FIXME: remove the apply?
- ;; (pk 'primcall name args result)
- (apply inferrer types succ
- (if result
- (append args (list result))
- args))))
- (result
- (adjoin-var types result all-types-entry))
- (else
- types)))
-
- (define (vector-replace vec idx val)
- (let ((vec (vector-copy vec)))
- (vector-set! vec idx val)
- vec))
-
- (define (update-out-types label typev types succ-idx)
- (let* ((entry (get-entry typev label))
- (old-types (vector-ref entry (1+ succ-idx))))
- (if (eq? types old-types)
- (values typev #f)
- (let ((entry (vector-replace entry (1+ succ-idx) types))
- (first? (not old-types)))
- (values (intmap-replace typev label entry) first?)))))
-
- (define (update-in-types label typev types saturate?)
- (let*-values (((typev entry) (ensure-entry typev label))
- ((old-types) (vector-ref entry 0))
- ;; TODO: If the label has only one predecessor, we can
- ;; avoid the meet.
- ((types) (if (not old-types)
- types
- (let ((meet (if saturate?
- type-entry-saturating-union
- type-entry-union)))
- (intmap-intersect old-types types meet)))))
- (if (eq? old-types types)
- (values typev #f)
- (let ((entry (vector-replace entry 0 types)))
- (values (intmap-replace typev label entry) #t)))))
-
- (define (propagate-types label typev succ-idx succ-label types)
- (let*-values
- (((typev first?) (update-out-types label typev types succ-idx))
- ((saturate?) (and (not first?) (<= succ-label label)))
- ((typev changed?) (update-in-types succ-label typev types saturate?)))
- (values (if changed? (list succ-label) '()) typev)))
-
- (define (visit-exp label typev k types exp)
- (define (propagate1 succ-label types)
- (propagate-types label typev 0 succ-label types))
- (define (propagate2 succ0-label types0 succ1-label types1)
- (let*-values (((changed0 typev)
- (propagate-types label typev 0 succ0-label types0))
- ((changed1 typev)
- (propagate-types label typev 1 succ1-label types1)))
- (values (append changed0 changed1) typev)))
- ;; Each of these branches must propagate to its successors.
- (match exp
- (($ $branch kt ($ $values (arg)))
- ;; The "normal" continuation is the #f branch.
- (let ((kf-types (restrict-var types arg
- (make-type-entry (logior &false &nil)
- 0
- 0)))
- (kt-types (restrict-var types arg
- (make-type-entry
- (logand &all-types
- (lognot (logior &false &nil)))
- -inf.0 +inf.0))))
- (propagate2 k kf-types kt kt-types)))
- (($ $branch kt ($ $primcall name args))
- ;; The "normal" continuation is the #f branch.
- (let ((kf-types (infer-primcall types 0 name args #f))
- (kt-types (infer-primcall types 1 name args #f)))
- (propagate2 k kf-types kt kt-types)))
- (($ $prompt escape? tag handler)
- ;; The "normal" continuation enters the prompt.
- (propagate2 k types handler types))
- (($ $primcall name args)
- (propagate1 k
- (match (intmap-ref conts k)
- (($ $kargs _ defs)
- (infer-primcall types 0 name args
- (match defs ((var) var) (() #f))))
- (_
- ;; (pk 'warning-no-restrictions name)
- types))))
- (($ $values args)
- (match (intmap-ref conts k)
- (($ $kargs _ defs)
- (let ((in types))
- (let lp ((defs defs) (args args) (out types))
- (match (cons defs args)
- ((() . ())
- (propagate1 k out))
- (((def . defs) . (arg . args))
- (lp defs args
- (adjoin-var out def (var-type-entry in arg))))))))
- (_
- (propagate1 k types))))
- ((or ($ $call) ($ $callk))
- (propagate1 k types))
- (($ $rec names vars funs)
- (let ((proc-type (make-type-entry &procedure -inf.0 +inf.0)))
- (propagate1 k (adjoin-vars types vars proc-type))))
- (_
- (match (intmap-ref conts k)
- (($ $kargs (_) (var))
- (let ((entry (match exp
- (($ $const val)
- (constant-type val))
- ((or ($ $prim) ($ $fun) ($ $closure))
- ;; Could be more precise here.
- (make-type-entry &procedure -inf.0 +inf.0)))))
- (propagate1 k (adjoin-var types var entry))))))))
-
- (define (visit-cont label typev)
- (let ((types (vector-ref (intmap-ref typev label) 0)))
- (define (propagate0)
- (values '() typev))
- (define (propagate1 succ-label types)
- (propagate-types label typev 0 succ-label types))
- (define (propagate2 succ0-label types0 succ1-label types1)
- (let*-values (((changed0 typev)
- (propagate-types label typev 0 succ0-label types0))
- ((changed1 typev)
- (propagate-types label typev 1 succ1-label types1)))
- (values (append changed0 changed1) typev)))
-
- ;; Add types for new definitions, and restrict types of
- ;; existing variables due to side effects.
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (visit-exp label typev k types exp))
- (($ $kreceive arity k)
- (match (intmap-ref conts k)
- (($ $kargs names vars)
- (propagate1 k (adjoin-vars types vars all-types-entry)))))
- (($ $kfun src meta self tail clause)
- (if clause
- (propagate1 clause (adjoin-var types self all-types-entry))
- (propagate0)))
- (($ $kclause arity kbody kalt)
- (match (intmap-ref conts kbody)
- (($ $kargs _ defs)
- (let ((body-types (adjoin-vars types defs all-types-entry)))
- (if kalt
- (propagate2 kbody body-types kalt types)
- (propagate1 kbody body-types))))))
- (($ $ktail) (propagate0)))))
-
- (worklist-fold* visit-cont
- (intset-add empty-intset kfun)
- (compute-initial-state)))
-
-(define (lookup-pre-type types label def)
- (let* ((entry (intmap-ref types label))
- (tentry (var-type-entry (vector-ref entry 0) def)))
- (values (type-entry-type tentry)
- (type-entry-min tentry)
- (type-entry-max tentry))))
-
-(define (lookup-post-type types label def succ-idx)
- (let* ((entry (intmap-ref types label))
- (tentry (var-type-entry (vector-ref entry (1+ succ-idx)) def)))
- (values (type-entry-type tentry)
- (type-entry-min tentry)
- (type-entry-max tentry))))
-
-(define (primcall-types-check? types label name args)
- (match (hashq-ref *type-checkers* name)
- (#f #f)
- (checker
- (let ((entry (intmap-ref types label)))
- (apply checker (vector-ref entry 0) args)))))
diff --git a/module/language/cps2/utils.scm b/module/language/cps2/utils.scm
deleted file mode 100644
index eae6b69c6..000000000
--- a/module/language/cps2/utils.scm
+++ /dev/null
@@ -1,477 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Helper facilities for working with CPS.
-;;;
-;;; Code:
-
-(define-module (language cps2 utils)
- #:use-module (ice-9 match)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (language cps2)
- #:use-module (language cps intset)
- #:use-module (language cps intmap)
- #:export (;; Fresh names.
- label-counter var-counter
- fresh-label fresh-var
- with-fresh-name-state compute-max-label-and-var
- let-fresh
-
- ;; Various utilities.
- fold1 fold2
- trivial-intset
- intmap-map
- intmap-keys
- invert-bijection invert-partition
- intset->intmap
- worklist-fold
- fixpoint
-
- ;; Flow analysis.
- compute-constant-values
- compute-function-body
- compute-reachable-functions
- compute-successors
- invert-graph
- compute-predecessors
- compute-reverse-post-order
- compute-strongly-connected-components
- compute-sorted-strongly-connected-components
- compute-idoms
- compute-dom-edges
- ))
-
-(define label-counter (make-parameter #f))
-(define var-counter (make-parameter #f))
-
-(define (fresh-label)
- (let ((count (or (label-counter)
- (error "fresh-label outside with-fresh-name-state"))))
- (label-counter (1+ count))
- count))
-
-(define (fresh-var)
- (let ((count (or (var-counter)
- (error "fresh-var outside with-fresh-name-state"))))
- (var-counter (1+ count))
- count))
-
-(define-syntax-rule (let-fresh (label ...) (var ...) body ...)
- (let* ((label (fresh-label)) ...
- (var (fresh-var)) ...)
- body ...))
-
-(define-syntax-rule (with-fresh-name-state fun body ...)
- (call-with-values (lambda () (compute-max-label-and-var fun))
- (lambda (max-label max-var)
- (parameterize ((label-counter (1+ max-label))
- (var-counter (1+ max-var)))
- body ...))))
-
-(define (compute-max-label-and-var conts)
- (values (or (intmap-prev conts) -1)
- (intmap-fold (lambda (k cont max-var)
- (match cont
- (($ $kargs names syms body)
- (apply max max-var syms))
- (($ $kfun src meta self)
- (max max-var self))
- (_ max-var)))
- conts
- -1)))
-
-(define-inlinable (fold1 f l s0)
- (let lp ((l l) (s0 s0))
- (match l
- (() s0)
- ((elt . l) (lp l (f elt s0))))))
-
-(define-inlinable (fold2 f l s0 s1)
- (let lp ((l l) (s0 s0) (s1 s1))
- (match l
- (() (values s0 s1))
- ((elt . l)
- (call-with-values (lambda () (f elt s0 s1))
- (lambda (s0 s1)
- (lp l s0 s1)))))))
-
-(define (trivial-intset set)
- "Returns the sole member of @var{set}, if @var{set} has exactly one
-member, or @code{#f} otherwise."
- (let ((first (intset-next set)))
- (and first
- (not (intset-next set (1+ first)))
- first)))
-
-(define (intmap-map proc map)
- (persistent-intmap
- (intmap-fold (lambda (k v out) (intmap-replace! out k (proc k v)))
- map
- map)))
-
-(define (intmap-keys map)
- "Return an intset of the keys in @var{map}."
- (persistent-intset
- (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
-
-(define (invert-bijection map)
- "Assuming the values of @var{map} are integers and are unique, compute
-a map in which each value maps to its key. If the values are not
-unique, an error will be signalled."
- (intmap-fold (lambda (k v out) (intmap-add out v k)) map empty-intmap))
-
-(define (invert-partition map)
- "Assuming the values of @var{map} are disjoint intsets, compute a map
-in which each member of each set maps to its key. If the values are not
-disjoint, an error will be signalled."
- (intmap-fold (lambda (k v* out)
- (intset-fold (lambda (v out) (intmap-add out v k)) v* out))
- map empty-intmap))
-
-(define (intset->intmap f set)
- (persistent-intmap
- (intset-fold (lambda (label preds)
- (intmap-add! preds label (f label)))
- set empty-intmap)))
-
-(define worklist-fold
- (case-lambda
- ((f in out)
- (let lp ((in in) (out out))
- (if (eq? in empty-intset)
- out
- (call-with-values (lambda () (f in out)) lp))))
- ((f in out0 out1)
- (let lp ((in in) (out0 out0) (out1 out1))
- (if (eq? in empty-intset)
- (values out0 out1)
- (call-with-values (lambda () (f in out0 out1)) lp))))))
-
-(define fixpoint
- (case-lambda
- ((f x)
- (let lp ((x x))
- (let ((x* (f x)))
- (if (eq? x x*) x* (lp x*)))))
- ((f x0 x1)
- (let lp ((x0 x0) (x1 x1))
- (call-with-values (lambda () (f x0 x1))
- (lambda (x0* x1*)
- (if (and (eq? x0 x0*) (eq? x1 x1*))
- (values x0* x1*)
- (lp x0* x1*))))))))
-
-(define (compute-defining-expressions conts)
- (define (meet-defining-expressions old new)
- ;; If there are multiple definitions, punt and
- ;; record #f.
- #f)
- (persistent-intmap
- (intmap-fold (lambda (label cont defs)
- (match cont
- (($ $kargs _ _ ($ $continue k src exp))
- (match (intmap-ref conts k)
- (($ $kargs (_) (var))
- (intmap-add! defs var exp meet-defining-expressions))
- (_ defs)))
- (_ defs)))
- conts
- empty-intmap)))
-
-(define (compute-constant-values conts)
- (persistent-intmap
- (intmap-fold (lambda (var exp out)
- (match exp
- (($ $const val)
- (intmap-add! out var val))
- (_ out)))
- (compute-defining-expressions conts)
- empty-intmap)))
-
-(define (compute-function-body conts kfun)
- (persistent-intset
- (let visit-cont ((label kfun) (labels empty-intset))
- (cond
- ((intset-ref labels label) labels)
- (else
- (let ((labels (intset-add! labels label)))
- (match (intmap-ref conts label)
- (($ $kreceive arity k) (visit-cont k labels))
- (($ $kfun src meta self ktail kclause)
- (let ((labels (visit-cont ktail labels)))
- (if kclause
- (visit-cont kclause labels)
- labels)))
- (($ $ktail) labels)
- (($ $kclause arity kbody kalt)
- (if kalt
- (visit-cont kalt (visit-cont kbody labels))
- (visit-cont kbody labels)))
- (($ $kargs names syms ($ $continue k src exp))
- (visit-cont k (match exp
- (($ $branch k)
- (visit-cont k labels))
- (($ $prompt escape? tag k)
- (visit-cont k labels))
- (_ labels)))))))))))
-
-(define (compute-reachable-functions conts kfun)
- "Compute a mapping LABEL->LABEL..., where each key is a reachable
-$kfun and each associated value is the body of the function, as an
-intset."
- (define (intset-cons i set) (intset-add set i))
- (define (visit-fun kfun body to-visit)
- (intset-fold
- (lambda (label to-visit)
- (define (return kfun*) (fold intset-cons to-visit kfun*))
- (define (return1 kfun) (intset-add to-visit kfun))
- (define (return0) to-visit)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- (($ $fun label) (return1 label))
- (($ $rec _ _ (($ $fun labels) ...)) (return labels))
- (($ $closure label nfree) (return1 label))
- (($ $callk label) (return1 label))
- (_ (return0))))
- (_ (return0))))
- body
- to-visit))
- (let lp ((to-visit (intset kfun)) (visited empty-intmap))
- (let ((to-visit (intset-subtract to-visit (intmap-keys visited))))
- (if (eq? to-visit empty-intset)
- visited
- (call-with-values
- (lambda ()
- (intset-fold
- (lambda (kfun to-visit visited)
- (let ((body (compute-function-body conts kfun)))
- (values (visit-fun kfun body to-visit)
- (intmap-add visited kfun body))))
- to-visit
- empty-intset
- visited))
- lp)))))
-
-(define* (compute-successors conts #:optional (kfun (intmap-next conts)))
- (define (visit label succs)
- (let visit ((label kfun) (succs empty-intmap))
- (define (propagate0)
- (intmap-add! succs label empty-intset))
- (define (propagate1 succ)
- (visit succ (intmap-add! succs label (intset succ))))
- (define (propagate2 succ0 succ1)
- (let ((succs (intmap-add! succs label (intset succ0 succ1))))
- (visit succ1 (visit succ0 succs))))
- (if (intmap-ref succs label (lambda (_) #f))
- succs
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (match exp
- (($ $branch kt) (propagate2 k kt))
- (($ $prompt escape? tag handler) (propagate2 k handler))
- (_ (propagate1 k))))
- (($ $kreceive arity k)
- (propagate1 k))
- (($ $kfun src meta self tail clause)
- (if clause
- (propagate2 clause tail)
- (propagate1 tail)))
- (($ $kclause arity kbody kalt)
- (if kalt
- (propagate2 kbody kalt)
- (propagate1 kbody)))
- (($ $ktail) (propagate0))))))
- (persistent-intmap (visit kfun empty-intmap)))
-
-(define* (compute-predecessors conts kfun #:key
- (labels (compute-function-body conts kfun)))
- (define (meet cdr car)
- (cons car cdr))
- (define (add-preds label preds)
- (define (add-pred k preds)
- (intmap-add! preds k label meet))
- (match (intmap-ref conts label)
- (($ $kreceive arity k)
- (add-pred k preds))
- (($ $kfun src meta self ktail kclause)
- (add-pred ktail (if kclause (add-pred kclause preds) preds)))
- (($ $ktail)
- preds)
- (($ $kclause arity kbody kalt)
- (add-pred kbody (if kalt (add-pred kalt preds) preds)))
- (($ $kargs names syms ($ $continue k src exp))
- (add-pred k
- (match exp
- (($ $branch k) (add-pred k preds))
- (($ $prompt _ _ k) (add-pred k preds))
- (_ preds))))))
- (persistent-intmap
- (intset-fold add-preds labels
- (intset->intmap (lambda (label) '()) labels))))
-
-(define (compute-reverse-post-order succs start)
- "Compute a reverse post-order numbering for a depth-first walk over
-nodes reachable from the start node."
- (let visit ((label start) (order '()) (visited empty-intset))
- (call-with-values
- (lambda ()
- (intset-fold (lambda (succ order visited)
- (if (intset-ref visited succ)
- (values order visited)
- (visit succ order visited)))
- (intmap-ref succs label)
- order
- (intset-add! visited label)))
- (lambda (order visited)
- ;; After visiting successors, add label to the reverse post-order.
- (values (cons label order) visited)))))
-
-(define (invert-graph succs)
- "Given a graph PRED->SUCC..., where PRED is a label and SUCC... is an
-intset of successors, return a graph SUCC->PRED...."
- (intmap-fold (lambda (pred succs preds)
- (intset-fold
- (lambda (succ preds)
- (intmap-add preds succ pred intset-add))
- succs
- preds))
- succs
- (intmap-map (lambda (label _) empty-intset) succs)))
-
-(define (compute-strongly-connected-components succs start)
- "Given a LABEL->SUCCESSOR... graph, compute a SCC->LABEL... map
-partitioning the labels into strongly connected components (SCCs)."
- (let ((preds (invert-graph succs)))
- (define (visit-scc scc sccs-by-label)
- (let visit ((label scc) (sccs-by-label sccs-by-label))
- (if (intmap-ref sccs-by-label label (lambda (_) #f))
- sccs-by-label
- (intset-fold visit
- (intmap-ref preds label)
- (intmap-add sccs-by-label label scc)))))
- (intmap-fold
- (lambda (label scc sccs)
- (let ((labels (intset-add empty-intset label)))
- (intmap-add sccs scc labels intset-union)))
- (fold visit-scc empty-intmap (compute-reverse-post-order succs start))
- empty-intmap)))
-
-(define (compute-sorted-strongly-connected-components edges)
- "Given a LABEL->SUCCESSOR... graph, return a list of strongly
-connected components in sorted order."
- (define nodes
- (intmap-keys edges))
- ;; Add a "start" node that links to all nodes in the graph, and then
- ;; remove it from the result.
- (define start
- (if (eq? nodes empty-intset)
- 0
- (1+ (intset-prev nodes))))
- (define components
- (intmap-remove
- (compute-strongly-connected-components (intmap-add edges start nodes)
- start)
- start))
- (define node-components
- (intmap-fold (lambda (id nodes out)
- (intset-fold (lambda (node out) (intmap-add out node id))
- nodes out))
- components
- empty-intmap))
- (define (node-component node)
- (intmap-ref node-components node))
- (define (component-successors id nodes)
- (intset-remove
- (intset-fold (lambda (node out)
- (intset-fold
- (lambda (successor out)
- (intset-add out (node-component successor)))
- (intmap-ref edges node)
- out))
- nodes
- empty-intset)
- id))
- (define component-edges
- (intmap-map component-successors components))
- (define preds
- (invert-graph component-edges))
- (define roots
- (intmap-fold (lambda (id succs out)
- (if (eq? empty-intset succs)
- (intset-add out id)
- out))
- component-edges
- empty-intset))
- ;; As above, add a "start" node that links to the roots, and remove it
- ;; from the result.
- (match (compute-reverse-post-order (intmap-add preds start roots) start)
- (((? (lambda (id) (eqv? id start))) . ids)
- (map (lambda (id) (intmap-ref components id)) ids))))
-
-;; Precondition: For each function in CONTS, the continuation names are
-;; topologically sorted.
-(define (compute-idoms conts kfun)
- ;; This is the iterative O(n^2) fixpoint algorithm, originally from
- ;; Allen and Cocke ("Graph-theoretic constructs for program flow
- ;; analysis", 1972). See the discussion in Cooper, Harvey, and
- ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
- (let ((preds-map (compute-predecessors conts kfun)))
- (define (compute-idom idoms preds)
- (define (idom-ref label)
- (intmap-ref idoms label (lambda (_) #f)))
- (match preds
- (() -1)
- ((pred) pred) ; Shortcut.
- ((pred . preds)
- (define (common-idom d0 d1)
- ;; We exploit the fact that a reverse post-order is a
- ;; topological sort, and so the idom of a node is always
- ;; numerically less than the node itself.
- (let lp ((d0 d0) (d1 d1))
- (cond
- ;; d0 or d1 can be false on the first iteration.
- ((not d0) d1)
- ((not d1) d0)
- ((= d0 d1) d0)
- ((< d0 d1) (lp d0 (idom-ref d1)))
- (else (lp (idom-ref d0) d1)))))
- (fold1 common-idom preds pred))))
- (define (adjoin-idom label preds idoms)
- (let ((idom (compute-idom idoms preds)))
- ;; Don't use intmap-add! here.
- (intmap-add idoms label idom (lambda (old new) new))))
- (fixpoint (lambda (idoms)
- (intmap-fold adjoin-idom preds-map idoms))
- empty-intmap)))
-
-;; Compute a vector containing, for each node, a list of the nodes that
-;; it immediately dominates. These are the "D" edges in the DJ tree.
-(define (compute-dom-edges idoms)
- (define (snoc cdr car) (cons car cdr))
- (persistent-intmap
- (intmap-fold (lambda (label idom doms)
- (let ((doms (intmap-add! doms label '())))
- (cond
- ((< idom 0) doms) ;; No edge to entry.
- (else (intmap-add! doms idom label snoc)))))
- idoms
- empty-intmap)))
diff --git a/module/language/cps2/verify.scm b/module/language/cps2/verify.scm
deleted file mode 100644
index 79b43f4fd..000000000
--- a/module/language/cps2/verify.scm
+++ /dev/null
@@ -1,306 +0,0 @@
-;;; Diagnostic checker for CPS
-;;; Copyright (C) 2014, 2015 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 program. If not, see
-;;; .
-
-;;; Commentary:
-;;;
-;;; A routine to detect invalid CPS.
-;;;
-;;; Code:
-
-(define-module (language cps2 verify)
- #:use-module (ice-9 match)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:use-module (language cps primitives)
- #:use-module (srfi srfi-11)
- #:export (verify))
-
-(define (intset-pop set)
- (match (intset-next set)
- (#f (values set #f))
- (i (values (intset-remove set i) i))))
-
-(define-syntax-rule (make-worklist-folder* seed ...)
- (lambda (f worklist seed ...)
- (let lp ((worklist worklist) (seed seed) ...)
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist i)
- (if i
- (call-with-values (lambda () (f i seed ...))
- (lambda (i* seed ...)
- (let add ((i* i*) (worklist worklist))
- (match i*
- (() (lp worklist seed ...))
- ((i . i*) (add i* (intset-add worklist i)))))))
- (values seed ...)))))))
-
-(define worklist-fold*
- (case-lambda
- ((f worklist seed)
- ((make-worklist-folder* seed) f worklist seed))))
-
-(define (check-distinct-vars conts)
- (define (adjoin-def var seen)
- (when (intset-ref seen var)
- (error "duplicate var name" seen var))
- (intset-add seen var))
- (intmap-fold
- (lambda (label cont seen)
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (fold1 adjoin-def vars seen))
- (($ $kfun src meta self tail clause)
- (adjoin-def self seen))
- (_ seen))
- )
- conts
- empty-intset))
-
-(define (compute-available-definitions conts kfun)
- "Compute and return a map of LABEL->VAR..., where VAR... are the
-definitions that are available at LABEL."
- (define (adjoin-def var defs)
- (when (intset-ref defs var)
- (error "var already present in defs" defs var))
- (intset-add defs var))
-
- (define (propagate defs succ out)
- (let* ((in (intmap-ref defs succ (lambda (_) #f)))
- (in* (if in (intset-intersect in out) out)))
- (if (eq? in in*)
- (values '() defs)
- (values (list succ)
- (intmap-add defs succ in* (lambda (old new) new))))))
-
- (define (visit-cont label defs)
- (let ((in (intmap-ref defs label)))
- (define (propagate0 out)
- (values '() defs))
- (define (propagate1 succ out)
- (propagate defs succ out))
- (define (propagate2 succ0 succ1 out)
- (let*-values (((changed0 defs) (propagate defs succ0 out))
- ((changed1 defs) (propagate defs succ1 out)))
- (values (append changed0 changed1) defs)))
-
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (let ((out (fold1 adjoin-def vars in)))
- (match exp
- (($ $branch kt) (propagate2 k kt out))
- (($ $prompt escape? tag handler) (propagate2 k handler out))
- (_ (propagate1 k out)))))
- (($ $kreceive arity k)
- (propagate1 k in))
- (($ $kfun src meta self tail clause)
- (let ((out (adjoin-def self in)))
- (if clause
- (propagate1 clause out)
- (propagate0 out))))
- (($ $kclause arity kbody kalt)
- (if kalt
- (propagate2 kbody kalt in)
- (propagate1 kbody in)))
- (($ $ktail) (propagate0 in)))))
-
- (worklist-fold* visit-cont
- (intset kfun)
- (intmap-add empty-intmap kfun empty-intset)))
-
-(define (intmap-for-each f map)
- (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
-
-(define (check-valid-var-uses conts kfun)
- (define (adjoin-def var defs) (intset-add defs var))
- (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
- (define (visit-exp exp bound first-order)
- (define (check-use var)
- (unless (intset-ref bound var)
- (error "unbound var" var)))
- (define (visit-first-order kfun)
- (if (intset-ref first-order kfun)
- first-order
- (visit-fun kfun empty-intset (intset-add first-order kfun))))
- (match exp
- ((or ($ $const) ($ $prim)) first-order)
- ;; todo: $closure
- (($ $fun kfun)
- (visit-fun kfun bound first-order))
- (($ $closure kfun)
- (visit-first-order kfun))
- (($ $rec names vars (($ $fun kfuns) ...))
- (let ((bound (fold1 adjoin-def vars bound)))
- (fold1 (lambda (kfun first-order)
- (visit-fun kfun bound first-order))
- kfuns first-order)))
- (($ $values args)
- (for-each check-use args)
- first-order)
- (($ $call proc args)
- (check-use proc)
- (for-each check-use args)
- first-order)
- (($ $callk kfun proc args)
- (check-use proc)
- (for-each check-use args)
- (visit-first-order kfun))
- (($ $branch kt ($ $values (arg)))
- (check-use arg)
- first-order)
- (($ $branch kt ($ $primcall name args))
- (for-each check-use args)
- first-order)
- (($ $primcall name args)
- (for-each check-use args)
- first-order)
- (($ $prompt escape? tag handler)
- (check-use tag)
- first-order)))
- (intmap-fold
- (lambda (label bound first-order)
- (let ((bound (intset-union free bound)))
- (match (intmap-ref conts label)
- (($ $kargs names vars ($ $continue k src exp))
- (visit-exp exp (fold1 adjoin-def vars bound) first-order))
- (_ first-order))))
- (compute-available-definitions conts kfun)
- first-order)))
-
-(define (check-label-partition conts kfun)
- ;; A continuation can only belong to one function.
- (intmap-fold
- (lambda (kfun body seen)
- (intset-fold
- (lambda (label seen)
- (intmap-add seen label kfun
- (lambda (old new)
- (error "label used by two functions" label old new))))
- body
- seen))
- (compute-reachable-functions conts kfun)
- empty-intmap))
-
-(define (compute-reachable-labels conts kfun)
- (intmap-fold (lambda (kfun body seen) (intset-union seen body))
- (compute-reachable-functions conts kfun)
- empty-intset))
-
-(define (check-arities conts kfun)
- (define (check-arity exp cont)
- (define (assert-unary)
- (match cont
- (($ $kargs (_) (_)) #t)
- (_ (error "expected unary continuation" cont))))
- (define (assert-nullary)
- (match cont
- (($ $kargs () ()) #t)
- (_ (error "expected unary continuation" cont))))
- (define (assert-n-ary n)
- (match cont
- (($ $kargs names vars)
- (unless (= (length vars) n)
- (error "expected n-ary continuation" n cont)))
- (_ (error "expected $kargs continuation" cont))))
- (define (assert-kreceive-or-ktail)
- (match cont
- ((or ($ $kreceive) ($ $ktail)) #t)
- (_ (error "expected $kreceive or $ktail continuation" cont))))
- (match exp
- ((or ($ $const) ($ $prim) ($ $closure) ($ $fun))
- (assert-unary))
- (($ $rec names vars funs)
- (unless (= (length names) (length vars) (length funs))
- (error "invalid $rec" exp))
- (assert-n-ary (length names))
- (match cont
- (($ $kargs names vars*)
- (unless (equal? vars* vars)
- (error "bound variable mismatch" vars vars*)))))
- (($ $values args)
- (match cont
- (($ $ktail) #t)
- (_ (assert-n-ary (length args)))))
- (($ $call proc args)
- (assert-kreceive-or-ktail))
- (($ $callk k proc args)
- (assert-kreceive-or-ktail))
- (($ $branch kt exp)
- (assert-nullary)
- (match (intmap-ref conts kt)
- (($ $kargs () ()) #t)
- (cont (error "bad kt" cont))))
- (($ $primcall name args)
- (match cont
- (($ $kargs names)
- (match (prim-arity name)
- ((out . in)
- (unless (= in (length args))
- (error "bad arity to primcall" name args in))
- (unless (= out (length names))
- (error "bad return arity from primcall" name names out)))))
- (($ $kreceive)
- (when (false-if-exception (prim-arity name))
- (error "primitive should continue to $kargs, not $kreceive" name)))
- (($ $ktail)
- (unless (eq? name 'return)
- (when (false-if-exception (prim-arity name))
- (error "primitive should continue to $kargs, not $ktail" name))))))
- (($ $prompt escape? tag handler)
- (assert-nullary)
- (match (intmap-ref conts handler)
- (($ $kreceive) #t)
- (cont (error "bad handler" cont))))))
- (let ((reachable (compute-reachable-labels conts kfun)))
- (intmap-for-each
- (lambda (label cont)
- (when (intset-ref reachable label)
- (match cont
- (($ $kargs names vars ($ $continue k src exp))
- (unless (= (length names) (length vars))
- (error "broken $kargs" label names vars))
- (check-arity exp (intmap-ref conts k)))
- (_ #t))))
- conts)))
-
-(define (check-functions-bound-once conts kfun)
- (let ((reachable (compute-reachable-labels conts kfun)))
- (define (add-fun fun functions)
- (when (intset-ref functions fun)
- (error "function already bound" fun))
- (intset-add functions fun))
- (intmap-fold
- (lambda (label cont functions)
- (if (intset-ref reachable label)
- (match cont
- (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
- (add-fun kfun functions))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
- (fold1 add-fun kfuns functions))
- (_ functions))
- functions))
- conts
- empty-intset)))
-
-(define (verify conts)
- (check-distinct-vars conts)
- (check-label-partition conts 0)
- (check-valid-var-uses conts 0)
- (check-arities conts 0)
- (check-functions-bound-once conts 0)
- conts)
diff --git a/module/language/cps2/with-cps.scm b/module/language/cps2/with-cps.scm
deleted file mode 100644
index f14eb93c9..000000000
--- a/module/language/cps2/with-cps.scm
+++ /dev/null
@@ -1,145 +0,0 @@
-;;; Continuation-passing style (CPS) intermediate language (IL)
-
-;; Copyright (C) 2013, 2014, 2015 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:
-;;;
-;;; Guile's CPS language is a label->cont mapping, which seems simple
-;;; enough. However it's often cumbersome to thread around the output
-;;; CPS program when doing non-trivial transformations, or when building
-;;; a CPS program from scratch. For example, when visiting an
-;;; expression during CPS conversion, we usually already know the label
-;;; and the $kargs wrapper for the cont, and just need to know the body
-;;; of that cont. However when building the body of that possibly
-;;; nested Tree-IL expression we will also need to add conts to the
-;;; result, so really it's a process that takes an incoming program,
-;;; adds conts to that program, and returns the result program and the
-;;; result term.
-;;;
-;;; It's a bit treacherous to do in a functional style as once you start
-;;; adding to a program, you shouldn't add to previous versions of that
-;;; program. Getting that right in the context of this program seed
-;;; that is threaded through the conversion requires the use of a
-;;; pattern, with-cps.
-;;;
-;;; with-cps goes like this:
-;;;
-;;; (with-cps cps clause ... tail-clause)
-;;;
-;;; Valid clause kinds are:
-;;;
-;;; (letk LABEL CONT)
-;;; (setk LABEL CONT)
-;;; (letv VAR ...)
-;;; (let$ X (PROC ARG ...))
-;;;
-;;; letk and letv create fresh CPS labels and variable names,
-;;; respectively. Labels and vars bound by letk and letv are in scope
-;;; from their point of definition onward. letv just creates fresh
-;;; variable names for use in other parts of with-cps, while letk binds
-;;; fresh labels to values and adds them to the resulting program. The
-;;; right-hand-side of letk, CONT, is passed to build-cont, so it should
-;;; be a valid production of that language. setk is like letk but it
-;;; doesn't create a fresh label name.
-;;;
-;;; let$ delegates processing to a sub-computation. The form (PROC ARG
-;;; ...) is syntactically altered to be (PROC CPS ARG ...), where CPS is
-;;; the value of the program being built, at that point in the
-;;; left-to-right with-cps execution. That form is is expected to
-;;; evaluate to two values: the new CPS term, and the value to bind to
-;;; X. X is in scope for the following with-cps clauses. The name was
-;;; chosen because the $ is reminiscent of the $ in CPS data types.
-;;;
-;;; The result of the with-cps form is determined by the tail clause,
-;;; which may be of these kinds:
-;;;
-;;; ($ (PROC ARG ...))
-;;; (setk LABEL CONT)
-;;; EXP
-;;;
-;;; $ is like let$, but in tail position. If the tail clause is setk,
-;;; then only one value is returned, the resulting CPS program.
-;;; Otherwise EXP is any kind of expression, which should not add to the
-;;; resulting program. Ending the with-cps with EXP is equivalant to
-;;; returning (values CPS EXP).
-;;;
-;;; It's a bit of a monad, innit? Don't tell anyone though!
-;;;
-;;; Sometimes you need to just bind some constants to CPS values.
-;;; with-cps-constants is there for you. For example:
-;;;
-;;; (with-cps-constants cps ((foo 34))
-;;; (build-term ($values (foo))))
-;;;
-;;; The body of with-cps-constants is a with-cps clause, or a sequence
-;;; of such clauses. But usually you will want with-cps-constants
-;;; inside a with-cps, so it usually looks like this:
-;;;
-;;; (with-cps cps
-;;; ...
-;;; ($ (with-cps-constants ((foo 34))
-;;; (build-term ($values (foo))))))
-;;;
-;;; which is to say that the $ or the let$ adds the CPS argument for us.
-;;;
-;;; Code:
-
-(define-module (language cps2 with-cps)
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps intmap)
- #:export (with-cps with-cps-constants))
-
-(define-syntax with-cps
- (syntax-rules (letk setk letv let$ $)
- ((_ (exp ...) clause ...)
- (let ((cps (exp ...)))
- (with-cps cps clause ...)))
- ((_ cps (letk label cont) clause ...)
- (let-fresh (label) ()
- (with-cps (intmap-add! cps label (build-cont cont))
- clause ...)))
- ((_ cps (setk label cont))
- (intmap-add! cps label (build-cont cont)
- (lambda (old new) new)))
- ((_ cps (setk label cont) clause ...)
- (with-cps (with-cps cps (setk label cont))
- clause ...))
- ((_ cps (letv v ...) clause ...)
- (let-fresh () (v ...)
- (with-cps cps clause ...)))
- ((_ cps (let$ var (proc arg ...)) clause ...)
- (call-with-values (lambda () (proc cps arg ...))
- (lambda (cps var)
- (with-cps cps clause ...))))
- ((_ cps ($ (proc arg ...)))
- (proc cps arg ...))
- ((_ cps exp)
- (values cps exp))))
-
-(define-syntax with-cps-constants
- (syntax-rules ()
- ((_ cps () clause ...)
- (with-cps cps clause ...))
- ((_ cps ((var val) (var* val*) ...) clause ...)
- (let ((x val))
- (with-cps cps
- (letv var)
- (let$ body (with-cps-constants ((var* val*) ...)
- clause ...))
- (letk label ($kargs ('var) (var) ,body))
- (build-term ($continue label #f ($const x))))))))
diff --git a/module/language/tree-il/compile-cps2.scm b/module/language/tree-il/compile-cps.scm
similarity index 99%
rename from module/language/tree-il/compile-cps2.scm
rename to module/language/tree-il/compile-cps.scm
index 932a49d27..59d2d7d90 100644
--- a/module/language/tree-il/compile-cps2.scm
+++ b/module/language/tree-il/compile-cps.scm
@@ -49,20 +49,20 @@
;;;
;;; Code:
-(define-module (language tree-il compile-cps2)
+(define-module (language tree-il compile-cps)
#:use-module (ice-9 match)
#:use-module ((srfi srfi-1) #:select (fold filter-map))
#:use-module (srfi srfi-26)
#:use-module ((system foreign) #:select (make-pointer pointer->scm))
- #:use-module (language cps2)
- #:use-module (language cps2 utils)
- #:use-module (language cps2 with-cps)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
#:use-module (language cps primitives)
#:use-module (language tree-il analyze)
#:use-module (language tree-il optimize)
#:use-module (language tree-il)
#:use-module (language cps intmap)
- #:export (compile-cps2))
+ #:export (compile-cps))
;;; Guile's semantics are that a toplevel lambda captures a reference on
;;; the current module, and that all contained lambdas use that module
@@ -931,7 +931,7 @@ integer."
(_ exp)))
exp))
-(define (compile-cps2 exp env opts)
+(define (compile-cps exp env opts)
(values (cps-convert/thunk
(canonicalize (optimize-tree-il exp env opts)))
env
diff --git a/module/language/tree-il/spec.scm b/module/language/tree-il/spec.scm
index d1c7326fc..10c20a010 100644
--- a/module/language/tree-il/spec.scm
+++ b/module/language/tree-il/spec.scm
@@ -1,6 +1,6 @@
;;; Tree Intermediate Language
-;; Copyright (C) 2009, 2010, 2011, 2013 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2010, 2011, 2013, 2015 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
@@ -22,7 +22,7 @@
#:use-module (system base language)
#:use-module (system base pmatch)
#:use-module (language tree-il)
- #:use-module (language tree-il compile-cps2)
+ #:use-module (language tree-il compile-cps)
#:export (tree-il))
(define (write-tree-il exp . port)
@@ -42,5 +42,5 @@
#:printer write-tree-il
#:parser parse-tree-il
#:joiner join
- #:compilers `((cps2 . ,compile-cps2))
+ #:compilers `((cps . ,compile-cps))
#:for-humans? #f)
From 4aabc205cc488a5440a637a7ec4d842ea8647be6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 22 Jul 2015 18:27:37 +0200
Subject: [PATCH 033/865] Add missing files
Last commit meant to rename files, not delete them. Whoops!
---
module/language/cps.scm | 358 +++++
module/language/cps/closure-conversion.scm | 824 ++++++++++
module/language/cps/compile-bytecode.scm | 433 +++++
module/language/cps/constructors.scm | 98 ++
module/language/cps/contification.scm | 475 ++++++
module/language/cps/cse.scm | 449 ++++++
module/language/cps/dce.scm | 399 +++++
module/language/cps/effects-analysis.scm | 484 ++++++
module/language/cps/elide-values.scm | 88 ++
module/language/cps/optimize.scm | 106 ++
module/language/cps/prune-bailouts.scm | 86 +
.../language/cps/prune-top-level-scopes.scm | 63 +
module/language/cps/reify-primitives.scm | 167 ++
module/language/cps/renumber.scm | 217 +++
module/language/cps/self-references.scm | 79 +
module/language/cps/simplify.scm | 267 ++++
module/language/cps/slot-allocation.scm | 995 ++++++++++++
module/language/cps/spec.scm | 37 +
module/language/cps/specialize-primcalls.scm | 59 +
module/language/cps/split-rec.scm | 174 ++
module/language/cps/type-fold.scm | 425 +++++
module/language/cps/types.scm | 1408 +++++++++++++++++
module/language/cps/utils.scm | 477 ++++++
module/language/cps/verify.scm | 306 ++++
module/language/cps/with-cps.scm | 145 ++
25 files changed, 8619 insertions(+)
create mode 100644 module/language/cps.scm
create mode 100644 module/language/cps/closure-conversion.scm
create mode 100644 module/language/cps/compile-bytecode.scm
create mode 100644 module/language/cps/constructors.scm
create mode 100644 module/language/cps/contification.scm
create mode 100644 module/language/cps/cse.scm
create mode 100644 module/language/cps/dce.scm
create mode 100644 module/language/cps/effects-analysis.scm
create mode 100644 module/language/cps/elide-values.scm
create mode 100644 module/language/cps/optimize.scm
create mode 100644 module/language/cps/prune-bailouts.scm
create mode 100644 module/language/cps/prune-top-level-scopes.scm
create mode 100644 module/language/cps/reify-primitives.scm
create mode 100644 module/language/cps/renumber.scm
create mode 100644 module/language/cps/self-references.scm
create mode 100644 module/language/cps/simplify.scm
create mode 100644 module/language/cps/slot-allocation.scm
create mode 100644 module/language/cps/spec.scm
create mode 100644 module/language/cps/specialize-primcalls.scm
create mode 100644 module/language/cps/split-rec.scm
create mode 100644 module/language/cps/type-fold.scm
create mode 100644 module/language/cps/types.scm
create mode 100644 module/language/cps/utils.scm
create mode 100644 module/language/cps/verify.scm
create mode 100644 module/language/cps/with-cps.scm
diff --git a/module/language/cps.scm b/module/language/cps.scm
new file mode 100644
index 000000000..b66bc38c0
--- /dev/null
+++ b/module/language/cps.scm
@@ -0,0 +1,358 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; This is the continuation-passing style (CPS) intermediate language
+;;; (IL) for Guile.
+;;;
+;;; In CPS, a term is a labelled expression that calls a continuation.
+;;; A function is a collection of terms. No term belongs to more than
+;;; one function. The function is identified by the label of its entry
+;;; term, and its body is composed of those terms that are reachable
+;;; from the entry term. A program is a collection of functions,
+;;; identified by the entry label of the entry function.
+;;;
+;;; Terms are themselves wrapped in continuations, which specify how
+;;; predecessors may continue to them. For example, a $kargs
+;;; continuation specifies that the term may be called with a specific
+;;; number of values, and that those values will then be bound to
+;;; lexical variables. $kreceive specifies that some number of values
+;;; will be passed on the stack, as from a multiple-value return. Those
+;;; values will be passed to a $kargs, if the number of values is
+;;; compatible with the $kreceive's arity. $kfun is an entry point to a
+;;; function, and receives arguments according to a well-known calling
+;;; convention (currently, on the stack) and the stack before
+;;; dispatching to a $kclause. A $kclause is a case-lambda clause, and
+;;; only appears within a $kfun; it checks the incoming values for the
+;;; correct arity and dispatches to a $kargs, or to the next clause.
+;;; Finally, $ktail is the tail continuation for a function, and
+;;; contains no term.
+;;;
+;;; Each continuation has a label that is unique in the program. As an
+;;; implementation detail, the labels are integers, which allows us to
+;;; easily sort them topologically. A program is a map from integers to
+;;; continuations, where continuation 0 in the map is the entry point
+;;; for the program, and is a $kfun of no arguments.
+;;;
+;;; $continue nodes call continuations. The expression contained in the
+;;; $continue node determines the value or values that are passed to the
+;;; target continuation: $const to pass a constant value, $values to
+;;; pass multiple named values, etc. $continue nodes also record the
+;;; source location corresponding to the expression.
+;;;
+;;; As mentioned above, a $kargs continuation can bind variables, if it
+;;; receives incoming values. $kfun also binds a value, corresponding
+;;; to the closure being called. A traditional CPS implementation will
+;;; nest terms in each other, binding them in "let" forms, ensuring that
+;;; continuations are declared and bound within the scope of the values
+;;; that they may use. In this way, the scope tree is a proof that
+;;; variables are defined before they are used. However, this proof is
+;;; conservative; it is possible for a variable to always be defined
+;;; before it is used, but not to be in scope:
+;;;
+;;; (letrec ((k1 (lambda (v1) (k2)))
+;;; (k2 (lambda () v1)))
+;;; (k1 0))
+;;;
+;;; This example is invalid, as v1 is used outside its scope. However
+;;; it would be perfectly fine for k2 to use v1 if k2 were nested inside
+;;; k1:
+;;;
+;;; (letrec ((k1 (lambda (v1)
+;;; (letrec ((k2 (lambda () v1)))
+;;; (k2))))
+;;; (k1 0))
+;;;
+;;; Because program transformation usually uses flow-based analysis,
+;;; having to update the scope tree to manifestly prove a transformation
+;;; that has already proven correct is needless overhead, and in the
+;;; worst case can prevent optimizations from occuring. For that
+;;; reason, Guile's CPS language does not nest terms. Instead, we use
+;;; the invariant that definitions must dominate uses. To check the
+;;; validity of a CPS program is thus more involved than checking for a
+;;; well-scoped tree; you have to do flow analysis to determine a
+;;; dominator tree. However the flexibility that this grants us is
+;;; worth the cost of throwing away the embedded proof of the scope
+;;; tree.
+;;;
+;;; This particular formulation of CPS was inspired by Andrew Kennedy's
+;;; 2007 paper, "Compiling with Continuations, Continued". All Guile
+;;; hackers should read that excellent paper! As in Kennedy's paper,
+;;; continuations are second-class, and may be thought of as basic block
+;;; labels. All values are bound to variables using continuation calls:
+;;; even constants!
+;;;
+;;; Finally, note that there are two flavors of CPS: higher-order and
+;;; first-order. By "higher-order", we mean that variables may be free
+;;; across function boundaries. Higher-order CPS contains $fun and $rec
+;;; expressions that declare functions in the scope of their term.
+;;; Closure conversion results in first-order CPS, where closure
+;;; representations have been explicitly chosen, and all variables used
+;;; in a function are bound. Higher-order CPS is good for
+;;; interprocedural optimizations like contification and beta reduction,
+;;; while first-order CPS is better for instruction selection, register
+;;; allocation, and code generation.
+;;;
+;;; See (language tree-il compile-cps) for details on how Tree-IL
+;;; converts to CPS.
+;;;
+;;; Code:
+
+(define-module (language cps)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-9 gnu)
+ #:use-module (srfi srfi-11)
+ #:export (;; Helper.
+ $arity
+ make-$arity
+
+ ;; Continuations.
+ $kreceive $kargs $kfun $ktail $kclause
+
+ ;; Terms.
+ $continue
+
+ ;; Expressions.
+ $const $prim $fun $rec $closure $branch
+ $call $callk $primcall $values $prompt
+
+ ;; Building macros.
+ build-cont build-term build-exp
+ rewrite-cont rewrite-term rewrite-exp
+
+ ;; External representation.
+ parse-cps unparse-cps))
+
+;; FIXME: Use SRFI-99, when Guile adds it.
+(define-syntax define-record-type*
+ (lambda (x)
+ (define (id-append ctx . syms)
+ (datum->syntax ctx (apply symbol-append (map syntax->datum syms))))
+ (syntax-case x ()
+ ((_ name field ...)
+ (and (identifier? #'name) (and-map identifier? #'(field ...)))
+ (with-syntax ((cons (id-append #'name #'make- #'name))
+ (pred (id-append #'name #'name #'?))
+ ((getter ...) (map (lambda (f)
+ (id-append f #'name #'- f))
+ #'(field ...))))
+ #'(define-record-type name
+ (cons field ...)
+ pred
+ (field getter)
+ ...))))))
+
+(define-syntax-rule (define-cps-type name field ...)
+ (begin
+ (define-record-type* name field ...)
+ (set-record-type-printer! name print-cps)))
+
+(define (print-cps exp port)
+ (format port "#" (unparse-cps exp)))
+
+;; Helper.
+(define-record-type* $arity req opt rest kw allow-other-keys?)
+
+;; Continuations
+(define-cps-type $kreceive arity kbody)
+(define-cps-type $kargs names syms term)
+(define-cps-type $kfun src meta self ktail kclause)
+(define-cps-type $ktail)
+(define-cps-type $kclause arity kbody kalternate)
+
+;; Terms.
+(define-cps-type $continue k src exp)
+
+;; Expressions.
+(define-cps-type $const val)
+(define-cps-type $prim name)
+(define-cps-type $fun body) ; Higher-order.
+(define-cps-type $rec names syms funs) ; Higher-order.
+(define-cps-type $closure label nfree) ; First-order.
+(define-cps-type $branch kt exp)
+(define-cps-type $call proc args)
+(define-cps-type $callk k proc args) ; First-order.
+(define-cps-type $primcall name args)
+(define-cps-type $values args)
+(define-cps-type $prompt escape? tag handler)
+
+(define-syntax build-arity
+ (syntax-rules (unquote)
+ ((_ (unquote exp)) exp)
+ ((_ (req opt rest kw allow-other-keys?))
+ (make-$arity req opt rest kw allow-other-keys?))))
+
+(define-syntax build-cont
+ (syntax-rules (unquote $kreceive $kargs $kfun $ktail $kclause)
+ ((_ (unquote exp))
+ exp)
+ ((_ ($kreceive req rest kargs))
+ (make-$kreceive (make-$arity req '() rest '() #f) kargs))
+ ((_ ($kargs (name ...) (unquote syms) body))
+ (make-$kargs (list name ...) syms (build-term body)))
+ ((_ ($kargs (name ...) (sym ...) body))
+ (make-$kargs (list name ...) (list sym ...) (build-term body)))
+ ((_ ($kargs names syms body))
+ (make-$kargs names syms (build-term body)))
+ ((_ ($kfun src meta self ktail kclause))
+ (make-$kfun src meta self ktail kclause))
+ ((_ ($ktail))
+ (make-$ktail))
+ ((_ ($kclause arity kbody kalternate))
+ (make-$kclause (build-arity arity) kbody kalternate))))
+
+(define-syntax build-term
+ (syntax-rules (unquote $rec $continue)
+ ((_ (unquote exp))
+ exp)
+ ((_ ($continue k src exp))
+ (make-$continue k src (build-exp exp)))))
+
+(define-syntax build-exp
+ (syntax-rules (unquote
+ $const $prim $fun $rec $closure $branch
+ $call $callk $primcall $values $prompt)
+ ((_ (unquote exp)) exp)
+ ((_ ($const val)) (make-$const val))
+ ((_ ($prim name)) (make-$prim name))
+ ((_ ($fun kentry)) (make-$fun kentry))
+ ((_ ($rec names gensyms funs)) (make-$rec names gensyms funs))
+ ((_ ($closure k nfree)) (make-$closure k nfree))
+ ((_ ($call proc (unquote args))) (make-$call proc args))
+ ((_ ($call proc (arg ...))) (make-$call proc (list arg ...)))
+ ((_ ($call proc args)) (make-$call proc args))
+ ((_ ($callk k proc (unquote args))) (make-$callk k proc args))
+ ((_ ($callk k proc (arg ...))) (make-$callk k proc (list arg ...)))
+ ((_ ($callk k proc args)) (make-$callk k proc args))
+ ((_ ($primcall name (unquote args))) (make-$primcall name args))
+ ((_ ($primcall name (arg ...))) (make-$primcall name (list arg ...)))
+ ((_ ($primcall name args)) (make-$primcall name args))
+ ((_ ($values (unquote args))) (make-$values args))
+ ((_ ($values (arg ...))) (make-$values (list arg ...)))
+ ((_ ($values args)) (make-$values args))
+ ((_ ($branch kt exp)) (make-$branch kt (build-exp exp)))
+ ((_ ($prompt escape? tag handler))
+ (make-$prompt escape? tag handler))))
+
+(define-syntax-rule (rewrite-cont x (pat cont) ...)
+ (match x
+ (pat (build-cont cont)) ...))
+(define-syntax-rule (rewrite-term x (pat term) ...)
+ (match x
+ (pat (build-term term)) ...))
+(define-syntax-rule (rewrite-exp x (pat body) ...)
+ (match x
+ (pat (build-exp body)) ...))
+
+(define (parse-cps exp)
+ (define (src exp)
+ (let ((props (source-properties exp)))
+ (and (pair? props) props)))
+ (match exp
+ ;; Continuations.
+ (('kreceive req rest k)
+ (build-cont ($kreceive req rest k)))
+ (('kargs names syms body)
+ (build-cont ($kargs names syms ,(parse-cps body))))
+ (('kfun src meta self ktail kclause)
+ (build-cont ($kfun (src exp) meta self ktail kclause)))
+ (('ktail)
+ (build-cont ($ktail)))
+ (('kclause (req opt rest kw allow-other-keys?) kbody)
+ (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody #f)))
+ (('kclause (req opt rest kw allow-other-keys?) kbody kalt)
+ (build-cont ($kclause (req opt rest kw allow-other-keys?) kbody kalt)))
+
+ ;; Calls.
+ (('continue k exp)
+ (build-term ($continue k (src exp) ,(parse-cps exp))))
+ (('unspecified)
+ (build-exp ($const *unspecified*)))
+ (('const exp)
+ (build-exp ($const exp)))
+ (('prim name)
+ (build-exp ($prim name)))
+ (('fun kbody)
+ (build-exp ($fun kbody)))
+ (('closure k nfree)
+ (build-exp ($closure k nfree)))
+ (('rec (name sym fun) ...)
+ (build-exp ($rec name sym (map parse-cps fun))))
+ (('call proc arg ...)
+ (build-exp ($call proc arg)))
+ (('callk k proc arg ...)
+ (build-exp ($callk k proc arg)))
+ (('primcall name arg ...)
+ (build-exp ($primcall name arg)))
+ (('branch k exp)
+ (build-exp ($branch k ,(parse-cps exp))))
+ (('values arg ...)
+ (build-exp ($values arg)))
+ (('prompt escape? tag handler)
+ (build-exp ($prompt escape? tag handler)))
+ (_
+ (error "unexpected cps" exp))))
+
+(define (unparse-cps exp)
+ (match exp
+ ;; Continuations.
+ (($ $kreceive ($ $arity req () rest () #f) k)
+ `(kreceive ,req ,rest ,k))
+ (($ $kargs names syms body)
+ `(kargs ,names ,syms ,(unparse-cps body)))
+ (($ $kfun src meta self ktail kclause)
+ `(kfun ,meta ,self ,ktail ,kclause))
+ (($ $ktail)
+ `(ktail))
+ (($ $kclause ($ $arity req opt rest kw allow-other-keys?) kbody kalternate)
+ `(kclause (,req ,opt ,rest ,kw ,allow-other-keys?) ,kbody
+ . ,(if kalternate (list kalternate) '())))
+
+ ;; Calls.
+ (($ $continue k src exp)
+ `(continue ,k ,(unparse-cps exp)))
+ (($ $const val)
+ (if (unspecified? val)
+ '(unspecified)
+ `(const ,val)))
+ (($ $prim name)
+ `(prim ,name))
+ (($ $fun kbody)
+ `(fun ,kbody))
+ (($ $closure k nfree)
+ `(closure ,k ,nfree))
+ (($ $rec names syms funs)
+ `(rec ,@(map (lambda (name sym fun)
+ (list name sym (unparse-cps fun)))
+ names syms funs)))
+ (($ $call proc args)
+ `(call ,proc ,@args))
+ (($ $callk k proc args)
+ `(callk ,k ,proc ,@args))
+ (($ $primcall name args)
+ `(primcall ,name ,@args))
+ (($ $branch k exp)
+ `(branch ,k ,(unparse-cps exp)))
+ (($ $values args)
+ `(values ,@args))
+ (($ $prompt escape? tag handler)
+ `(prompt ,escape? ,tag ,handler))
+ (_
+ (error "unexpected cps" exp))))
diff --git a/module/language/cps/closure-conversion.scm b/module/language/cps/closure-conversion.scm
new file mode 100644
index 000000000..29577a99c
--- /dev/null
+++ b/module/language/cps/closure-conversion.scm
@@ -0,0 +1,824 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; This pass converts a CPS term in such a way that no function has any
+;;; free variables. Instead, closures are built explicitly with
+;;; make-closure primcalls, and free variables are referenced through
+;;; the closure.
+;;;
+;;; Closure conversion also removes any $rec expressions that
+;;; contification did not handle. See (language cps) for a further
+;;; discussion of $rec.
+;;;
+;;; Code:
+
+(define-module (language cps closure-conversion)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (fold
+ filter-map
+ ))
+ #:use-module (srfi srfi-11)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (convert-closures))
+
+(define (compute-function-bodies conts kfun)
+ "Compute a map from FUN-LABEL->BODY-LABEL... for all $fun instances in
+conts."
+ (let visit-fun ((kfun kfun) (out empty-intmap))
+ (let ((body (compute-function-body conts kfun)))
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
+ (visit-fun kfun out))
+ (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
+ (fold visit-fun out kfun))
+ (_ out)))
+ body
+ (intmap-add out kfun body)))))
+
+(define (compute-program-body functions)
+ (intmap-fold (lambda (label body out) (intset-union body out))
+ functions
+ empty-intset))
+
+(define (filter-reachable conts functions)
+ (let ((reachable (compute-program-body functions)))
+ (intmap-fold
+ (lambda (label cont out)
+ (if (intset-ref reachable label)
+ out
+ (intmap-remove out label)))
+ conts conts)))
+
+(define (compute-non-operator-uses conts)
+ (persistent-intset
+ (intmap-fold
+ (lambda (label cont uses)
+ (define (add-use var uses) (intset-add! uses var))
+ (define (add-uses vars uses)
+ (match vars
+ (() uses)
+ ((var . vars) (add-uses vars (add-use var uses)))))
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) uses)
+ (($ $values args)
+ (add-uses args uses))
+ (($ $call proc args)
+ (add-uses args uses))
+ (($ $branch kt ($ $values (arg)))
+ (add-use arg uses))
+ (($ $branch kt ($ $primcall name args))
+ (add-uses args uses))
+ (($ $primcall name args)
+ (add-uses args uses))
+ (($ $prompt escape? tag handler)
+ (add-use tag uses))))
+ (_ uses)))
+ conts
+ empty-intset)))
+
+(define (compute-singly-referenced-labels conts body)
+ (define (add-ref label single multiple)
+ (define (ref k single multiple)
+ (if (intset-ref single k)
+ (values single (intset-add! multiple k))
+ (values (intset-add! single k) multiple)))
+ (define (ref0) (values single multiple))
+ (define (ref1 k) (ref k single multiple))
+ (define (ref2 k k*)
+ (if k*
+ (let-values (((single multiple) (ref k single multiple)))
+ (ref k* single multiple))
+ (ref1 k)))
+ (match (intmap-ref conts label)
+ (($ $kreceive arity k) (ref1 k))
+ (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
+ (($ $ktail) (ref0))
+ (($ $kclause arity kbody kalt) (ref2 kbody kalt))
+ (($ $kargs names syms ($ $continue k src exp))
+ (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
+ (let*-values (((single multiple) (values empty-intset empty-intset))
+ ((single multiple) (intset-fold add-ref body single multiple)))
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple))))
+
+(define (compute-function-names conts functions)
+ "Compute a map of FUN-LABEL->BOUND-VAR... for each labelled function
+whose bound vars we know."
+ (define (add-named-fun var kfun out)
+ (let ((self (match (intmap-ref conts kfun)
+ (($ $kfun src meta self) self))))
+ (intmap-add out kfun (intset var self))))
+ (intmap-fold
+ (lambda (label body out)
+ (let ((single (compute-singly-referenced-labels conts body)))
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue k _ ($ $fun kfun)))
+ (if (intset-ref single k)
+ (match (intmap-ref conts k)
+ (($ $kargs (_) (var)) (add-named-fun var kfun out))
+ (_ out))
+ out))
+ (($ $kargs _ _ ($ $continue k _ ($ $rec _ vars (($ $fun kfun) ...))))
+ (unless (intset-ref single k)
+ (error "$rec continuation has multiple predecessors??"))
+ (fold add-named-fun out vars kfun))
+ (_ out)))
+ body
+ out)))
+ functions
+ empty-intmap))
+
+(define (compute-well-known-functions conts bound->label)
+ "Compute a set of labels indicating the well-known functions in
+@var{conts}. A well-known function is a function whose bound names we
+know and which is never used in a non-operator position."
+ (intset-subtract
+ (persistent-intset
+ (intmap-fold (lambda (bound label candidates)
+ (intset-add! candidates label))
+ bound->label
+ empty-intset))
+ (persistent-intset
+ (intset-fold (lambda (var not-well-known)
+ (match (intmap-ref bound->label var (lambda (_) #f))
+ (#f not-well-known)
+ (label (intset-add! not-well-known label))))
+ (compute-non-operator-uses conts)
+ empty-intset))))
+
+(define (intset-cons i set)
+ (intset-add set i))
+
+(define (compute-shared-closures conts well-known)
+ "Compute a map LABEL->VAR indicating the sets of functions that will
+share a closure. If a functions's label is in the map, it is shared.
+The entries indicate the var of the shared closure, which will be one of
+the bound vars of the closure."
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs _ _
+ ($ $continue _ _ ($ $rec names vars (($ $fun kfuns) ...))))
+ ;; The split-rec pass should have ensured that this $rec forms a
+ ;; strongly-connected component, so the free variables from all of
+ ;; the functions will be alive as long as one of the closures is
+ ;; alive. For that reason we can consider storing all free
+ ;; variables in one closure and sharing it.
+ (let* ((kfuns-set (fold intset-cons empty-intset kfuns))
+ (unknown-kfuns (intset-subtract kfuns-set well-known)))
+ (cond
+ ((or (eq? empty-intset kfuns-set) (trivial-intset kfuns-set))
+ ;; There is only zero or one function bound here. Trivially
+ ;; shared already.
+ out)
+ ((eq? empty-intset unknown-kfuns)
+ ;; All functions are well-known; we can share a closure. Use
+ ;; the first bound variable.
+ (let ((closure (car vars)))
+ (intset-fold (lambda (kfun out)
+ (intmap-add out kfun closure))
+ kfuns-set out)))
+ ((trivial-intset unknown-kfuns)
+ => (lambda (unknown-kfun)
+ ;; Only one function is not-well-known. Use that
+ ;; function's closure as the shared closure.
+ (let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
+ (intset-fold (lambda (kfun out)
+ (intmap-add out kfun closure))
+ kfuns-set out))))
+ (else
+ ;; More than one not-well-known function means we need more
+ ;; than one proper closure, so we can't share.
+ out))))
+ (_ out)))
+ conts
+ empty-intmap))
+
+(define* (rewrite-shared-closure-calls cps functions label->bound shared kfun)
+ "Rewrite CPS such that every call to a function with a shared closure
+instead is a $callk to that label, but passing the shared closure as the
+proc argument. For recursive calls, use the appropriate 'self'
+variable, if possible. Also rewrite uses of the non-well-known but
+shared closures to use the appropriate 'self' variable, if possible."
+ ;; env := var -> (var . label)
+ (define (rewrite-fun kfun cps env)
+ (define (subst var)
+ (match (intmap-ref env var (lambda (_) #f))
+ (#f var)
+ ((var . label) var)))
+
+ (define (rename-exp label cps names vars k src exp)
+ (intmap-replace!
+ cps label
+ (build-cont
+ ($kargs names vars
+ ($continue k src
+ ,(rewrite-exp exp
+ ((or ($ $const) ($ $prim)) ,exp)
+ (($ $call proc args)
+ ,(let ((args (map subst args)))
+ (rewrite-exp (intmap-ref env proc (lambda (_) #f))
+ (#f ($call proc ,args))
+ ((closure . label) ($callk label closure ,args)))))
+ (($ $primcall name args)
+ ($primcall name ,(map subst args)))
+ (($ $branch k ($ $values (arg)))
+ ($branch k ($values ((subst arg)))))
+ (($ $branch k ($ $primcall name args))
+ ($branch k ($primcall name ,(map subst args))))
+ (($ $values args)
+ ($values ,(map subst args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (subst tag) handler))))))))
+
+ (define (visit-exp label cps names vars k src exp)
+ (define (compute-env label bound self rec-bound rec-labels env)
+ (define (add-bound-var bound label env)
+ (intmap-add env bound (cons self label) (lambda (old new) new)))
+ (if (intmap-ref shared label (lambda (_) #f))
+ ;; Within a function with a shared closure, rewrite
+ ;; references to bound vars to use the "self" var.
+ (fold add-bound-var env rec-bound rec-labels)
+ ;; Otherwise be sure to use "self" references in any
+ ;; closure.
+ (add-bound-var bound label env)))
+ (match exp
+ (($ $fun label)
+ (rewrite-fun label cps env))
+ (($ $rec names vars (($ $fun labels) ...))
+ (fold (lambda (label var cps)
+ (match (intmap-ref cps label)
+ (($ $kfun src meta self)
+ (rewrite-fun label cps
+ (compute-env label var self vars labels
+ env)))))
+ cps labels vars))
+ (_ (rename-exp label cps names vars k src exp))))
+
+ (define (rewrite-cont label cps)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-exp label cps names vars k src exp))
+ (_ cps)))
+
+ (intset-fold rewrite-cont (intmap-ref functions kfun) cps))
+
+ ;; Initial environment is bound-var -> (shared-var . label) map for
+ ;; functions with shared closures.
+ (let ((env (intmap-fold (lambda (label shared env)
+ (intset-fold (lambda (bound env)
+ (intmap-add env bound
+ (cons shared label)))
+ (intset-remove
+ (intmap-ref label->bound label)
+ (match (intmap-ref cps label)
+ (($ $kfun src meta self) self)))
+ env))
+ shared
+ empty-intmap)))
+ (persistent-intmap (rewrite-fun kfun cps env))))
+
+(define (compute-free-vars conts kfun shared)
+ "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
+references."
+ (define (add-def var defs) (intset-add! defs var))
+ (define (add-defs vars defs)
+ (match vars
+ (() defs)
+ ((var . vars) (add-defs vars (add-def var defs)))))
+ (define (add-use var uses)
+ (intset-add! uses var))
+ (define (add-uses vars uses)
+ (match vars
+ (() uses)
+ ((var . vars) (add-uses vars (add-use var uses)))))
+ (define (visit-nested-funs body)
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $fun kfun)))
+ (intmap-union out (visit-fun kfun)))
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $rec _ _ (($ $fun labels) ...))))
+ (let* ((out (fold (lambda (kfun out)
+ (intmap-union out (visit-fun kfun)))
+ out labels))
+ (free (fold (lambda (kfun free)
+ (intset-union free (intmap-ref out kfun)))
+ empty-intset labels)))
+ (fold (lambda (kfun out)
+ ;; For functions that share a closure, the free
+ ;; variables for one will be the union of the free
+ ;; variables for all.
+ (if (intmap-ref shared kfun (lambda (_) #f))
+ (intmap-replace out kfun free)
+ out))
+ out
+ labels)))
+ (_ out)))
+ body
+ empty-intmap))
+ (define (visit-fun kfun)
+ (let* ((body (compute-function-body conts kfun))
+ (free (visit-nested-funs body)))
+ (call-with-values
+ (lambda ()
+ (intset-fold
+ (lambda (label defs uses)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (values
+ (add-defs vars defs)
+ (match exp
+ ((or ($ $const) ($ $prim)) uses)
+ (($ $fun kfun)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ (($ $rec names vars (($ $fun kfun) ...))
+ (fold (lambda (kfun uses)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ uses kfun))
+ (($ $values args)
+ (add-uses args uses))
+ (($ $call proc args)
+ (add-use proc (add-uses args uses)))
+ (($ $callk label proc args)
+ (add-use proc (add-uses args uses)))
+ (($ $branch kt ($ $values (arg)))
+ (add-use arg uses))
+ (($ $branch kt ($ $primcall name args))
+ (add-uses args uses))
+ (($ $primcall name args)
+ (add-uses args uses))
+ (($ $prompt escape? tag handler)
+ (add-use tag uses)))))
+ (($ $kfun src meta self)
+ (values (add-def self defs) uses))
+ (_ (values defs uses))))
+ body empty-intset empty-intset))
+ (lambda (defs uses)
+ (intmap-add free kfun (intset-subtract
+ (persistent-intset uses)
+ (persistent-intset defs)))))))
+ (visit-fun kfun))
+
+(define (eliminate-closure? label free-vars)
+ (eq? (intmap-ref free-vars label) empty-intset))
+
+(define (closure-label label shared bound->label)
+ (cond
+ ((intmap-ref shared label (lambda (_) #f))
+ => (lambda (closure)
+ (intmap-ref bound->label closure)))
+ (else label)))
+
+(define (closure-alias label well-known free-vars)
+ (and (intset-ref well-known label)
+ (trivial-intset (intmap-ref free-vars label))))
+
+(define (prune-free-vars free-vars bound->label well-known shared)
+ "Given the label->bound-var map @var{free-vars}, remove free variables
+that are known functions with zero free variables, and replace
+references to well-known functions with one free variable with that free
+variable, until we reach a fixed point on the free-vars map."
+ (define (prune-free in-label free free-vars)
+ (intset-fold (lambda (var free)
+ (match (intmap-ref bound->label var (lambda (_) #f))
+ (#f free)
+ (label
+ (cond
+ ((eliminate-closure? label free-vars)
+ (intset-remove free var))
+ ((closure-alias (closure-label label shared bound->label)
+ well-known free-vars)
+ => (lambda (alias)
+ ;; If VAR is free in LABEL, then ALIAS must
+ ;; also be free because its definition must
+ ;; precede VAR's definition.
+ (intset-add (intset-remove free var) alias)))
+ (else free)))))
+ free free))
+ (fixpoint (lambda (free-vars)
+ (intmap-fold (lambda (label free free-vars)
+ (intmap-replace free-vars label
+ (prune-free label free free-vars)))
+ free-vars
+ free-vars))
+ free-vars))
+
+(define (intset-find set i)
+ (let lp ((idx 0) (start #f))
+ (let ((start (intset-next set start)))
+ (cond
+ ((not start) (error "not found" set i))
+ ((= start i) idx)
+ (else (lp (1+ idx) (1+ start)))))))
+
+(define (intset-count set)
+ (intset-fold (lambda (_ count) (1+ count)) set 0))
+
+(define (convert-one cps label body free-vars bound->label well-known shared)
+ (define (well-known? label)
+ (intset-ref well-known label))
+
+ (let* ((free (intmap-ref free-vars label))
+ (nfree (intset-count free))
+ (self-known? (well-known? (closure-label label shared bound->label)))
+ (self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
+ (define (convert-arg cps var k)
+ "Convert one possibly free variable reference to a bound reference.
+
+If @var{var} is free, it is replaced by a closure reference via a
+@code{free-ref} primcall, and @var{k} is called with the new var.
+Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
+ ;; We know that var is not the name of a well-known function.
+ (cond
+ ((and=> (intmap-ref bound->label var (lambda (_) #f))
+ (lambda (kfun)
+ (and (eq? empty-intset (intmap-ref free-vars kfun))
+ kfun)))
+ ;; A not-well-known function with zero free vars. Copy as a
+ ;; constant, relying on the linker to reify just one copy.
+ => (lambda (kfun)
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ (build-term ($continue k* #f ($closure kfun 0))))))
+ ((intset-ref free var)
+ (match (vector self-known? nfree)
+ (#(#t 1)
+ ;; A reference to the one free var of a well-known function.
+ (with-cps cps
+ ($ (k self))))
+ (#(#t 2)
+ ;; A reference to one of the two free vars in a well-known
+ ;; function.
+ (let ((op (if (= var (intset-next free)) 'car 'cdr)))
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ (build-term ($continue k* #f ($primcall op (self)))))))
+ (_
+ (let* ((idx (intset-find free var))
+ (op (cond
+ ((not self-known?) 'free-ref)
+ ((<= idx #xff) 'vector-ref/immediate)
+ (else 'vector-ref))))
+ (with-cps cps
+ (letv var*)
+ (let$ body (k var*))
+ (letk k* ($kargs (#f) (var*) ,body))
+ ($ (with-cps-constants ((idx idx))
+ (build-term
+ ($continue k* #f ($primcall op (self idx)))))))))))
+ (else
+ (with-cps cps
+ ($ (k var))))))
+
+ (define (convert-args cps vars k)
+ "Convert a number of possibly free references to bound references.
+@var{k} is called with the bound references, and should return the
+term."
+ (match vars
+ (()
+ (with-cps cps
+ ($ (k '()))))
+ ((var . vars)
+ (convert-arg cps var
+ (lambda (cps var)
+ (convert-args cps vars
+ (lambda (cps vars)
+ (with-cps cps
+ ($ (k (cons var vars)))))))))))
+
+ (define (allocate-closure cps k src label known? nfree)
+ "Allocate a new closure, and pass it to $var{k}."
+ (match (vector known? nfree)
+ (#(#f nfree)
+ ;; The call sites cannot be enumerated; allocate a closure.
+ (with-cps cps
+ (build-term ($continue k src ($closure label nfree)))))
+ (#(#t 2)
+ ;; Well-known closure with two free variables; the closure is a
+ ;; pair.
+ (with-cps cps
+ ($ (with-cps-constants ((false #f))
+ (build-term
+ ($continue k src ($primcall 'cons (false false))))))))
+ ;; Well-known callee with more than two free variables; the closure
+ ;; is a vector.
+ (#(#t nfree)
+ (unless (> nfree 2)
+ (error "unexpected well-known nullary, unary, or binary closure"))
+ (let ((op (if (<= nfree #xff) 'make-vector/immediate 'make-vector)))
+ (with-cps cps
+ ($ (with-cps-constants ((nfree nfree)
+ (false #f))
+ (build-term
+ ($continue k src ($primcall op (nfree false)))))))))))
+
+ (define (init-closure cps k src var known? free)
+ "Initialize the free variables @var{closure-free} in a closure
+bound to @var{var}, and continue to @var{k}."
+ (match (vector known? (intset-count free))
+ ;; Well-known callee with zero or one free variables; no
+ ;; initialization necessary.
+ (#(#t (or 0 1))
+ (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ ;; Well-known callee with two free variables; do a set-car! and
+ ;; set-cdr!.
+ (#(#t 2)
+ (let* ((free0 (intset-next free))
+ (free1 (intset-next free (1+ free0))))
+ (convert-arg cps free0
+ (lambda (cps v0)
+ (with-cps cps
+ (let$ body
+ (convert-arg free1
+ (lambda (cps v1)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($primcall 'set-cdr! (var v1))))))))
+ (letk kcdr ($kargs () () ,body))
+ (build-term
+ ($continue kcdr src ($primcall 'set-car! (var v0)))))))))
+ ;; Otherwise residualize a sequence of vector-set! or free-set!,
+ ;; depending on whether the callee is well-known or not.
+ (_
+ (let lp ((cps cps) (prev #f) (idx 0))
+ (match (intset-next free prev)
+ (#f (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ (v (with-cps cps
+ (let$ body (lp (1+ v) (1+ idx)))
+ (letk k ($kargs () () ,body))
+ ($ (convert-arg v
+ (lambda (cps v)
+ (with-cps cps
+ ($ (with-cps-constants ((idx idx))
+ (let ((op (cond
+ ((not known?) 'free-set!)
+ ((<= idx #xff) 'vector-set!/immediate)
+ (else 'vector-set!))))
+ (build-term
+ ($continue k src
+ ($primcall op (var idx v))))))))))))))))))
+
+ (define (make-single-closure cps k src kfun)
+ (let ((free (intmap-ref free-vars kfun)))
+ (match (vector (well-known? kfun) (intset-count free))
+ (#(#f 0)
+ (with-cps cps
+ (build-term ($continue k src ($closure kfun 0)))))
+ (#(#t 0)
+ (with-cps cps
+ (build-term ($continue k src ($const #f)))))
+ (#(#t 1)
+ ;; A well-known closure of one free variable is replaced
+ ;; at each use with the free variable itself, so we don't
+ ;; need a binding at all; and yet, the continuation
+ ;; expects one value, so give it something. DCE should
+ ;; clean up later.
+ (with-cps cps
+ (build-term ($continue k src ($const #f)))))
+ (#(well-known? nfree)
+ ;; A bit of a mess, but beta conversion should remove the
+ ;; final $values if possible.
+ (with-cps cps
+ (letv closure)
+ (letk k* ($kargs () () ($continue k src ($values (closure)))))
+ (let$ init (init-closure k* src closure well-known? free))
+ (letk knew ($kargs (#f) (closure) ,init))
+ ($ (allocate-closure knew src kfun well-known? nfree)))))))
+
+ ;; The callee is known, but not necessarily well-known.
+ (define (convert-known-proc-call cps k src label closure args)
+ (define (have-closure cps closure)
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($callk label closure args)))))))
+ (cond
+ ((eq? (intmap-ref free-vars label) empty-intset)
+ ;; Known call, no free variables; no closure needed.
+ ;; Pass #f as closure argument.
+ (with-cps cps
+ ($ (with-cps-constants ((false #f))
+ ($ (have-closure false))))))
+ ((and (well-known? (closure-label label shared bound->label))
+ (trivial-intset (intmap-ref free-vars label)))
+ ;; Well-known closures with one free variable are
+ ;; replaced at their use sites by uses of the one free
+ ;; variable.
+ => (lambda (var)
+ (convert-arg cps var have-closure)))
+ (else
+ ;; Otherwise just load the proc.
+ (convert-arg cps closure have-closure))))
+
+ (define (visit-term cps term)
+ (match term
+ (($ $continue k src (or ($ $const) ($ $prim)))
+ (with-cps cps
+ term))
+
+ (($ $continue k src ($ $fun kfun))
+ (with-cps cps
+ ($ (make-single-closure k src kfun))))
+
+ ;; Remove letrec.
+ (($ $continue k src ($ $rec names vars (($ $fun kfuns) ...)))
+ (match (vector names vars kfuns)
+ (#(() () ())
+ ;; Trivial empty case.
+ (with-cps cps
+ (build-term ($continue k src ($values ())))))
+ (#((name) (var) (kfun))
+ ;; Trivial single case. We have already proven that K has
+ ;; only LABEL as its predecessor, so we have been able
+ ;; already to rewrite free references to the bound name with
+ ;; the self name.
+ (with-cps cps
+ ($ (make-single-closure k src kfun))))
+ (#(_ _ (kfun0 . _))
+ ;; A non-trivial strongly-connected component. Does it have
+ ;; a shared closure?
+ (match (intmap-ref shared kfun0 (lambda (_) #f))
+ (#f
+ ;; Nope. Allocate closures for each function.
+ (let lp ((cps (match (intmap-ref cps k)
+ ;; Steal declarations from the continuation.
+ (($ $kargs names vals body)
+ (intmap-replace cps k
+ (build-cont
+ ($kargs () () ,body))))))
+ (in (map vector names vars kfuns))
+ (init (lambda (cps)
+ (with-cps cps
+ (build-term
+ ($continue k src ($values ())))))))
+ (match in
+ (() (init cps))
+ ((#(name var kfun) . in)
+ (let* ((known? (well-known? kfun))
+ (free (intmap-ref free-vars kfun))
+ (nfree (intset-count free)))
+ (define (next-init cps)
+ (with-cps cps
+ (let$ body (init))
+ (letk k ($kargs () () ,body))
+ ($ (init-closure k src var known? free))))
+ (with-cps cps
+ (let$ body (lp in next-init))
+ (letk k ($kargs (name) (var) ,body))
+ ($ (allocate-closure k src kfun known? nfree))))))))
+ (shared
+ ;; If shared is in the bound->var map, that means one of
+ ;; the functions is not well-known. Otherwise use kfun0
+ ;; as the function label, but just so make-single-closure
+ ;; can find the free vars, not for embedding in the
+ ;; closure.
+ (let* ((kfun (intmap-ref bound->label shared (lambda (_) kfun0)))
+ (cps (match (intmap-ref cps k)
+ ;; Make continuation declare only the shared
+ ;; closure.
+ (($ $kargs names vals body)
+ (intmap-replace cps k
+ (build-cont
+ ($kargs (#f) (shared) ,body)))))))
+ (with-cps cps
+ ($ (make-single-closure k src kfun)))))))))
+
+ (($ $continue k src ($ $call proc args))
+ (match (intmap-ref bound->label proc (lambda (_) #f))
+ (#f
+ (convert-arg cps proc
+ (lambda (cps proc)
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($call proc args)))))))))
+ (label
+ (convert-known-proc-call cps k src label proc args))))
+
+ (($ $continue k src ($ $callk label proc args))
+ (convert-known-proc-call cps k src label proc args))
+
+ (($ $continue k src ($ $primcall name args))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($primcall name args)))))))
+
+ (($ $continue k src ($ $branch kt ($ $primcall name args)))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($branch kt ($primcall name args))))))))
+
+ (($ $continue k src ($ $branch kt ($ $values (arg))))
+ (convert-arg cps arg
+ (lambda (cps arg)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($branch kt ($values (arg)))))))))
+
+ (($ $continue k src ($ $values args))
+ (convert-args cps args
+ (lambda (cps args)
+ (with-cps cps
+ (build-term
+ ($continue k src ($values args)))))))
+
+ (($ $continue k src ($ $prompt escape? tag handler))
+ (convert-arg cps tag
+ (lambda (cps tag)
+ (with-cps cps
+ (build-term
+ ($continue k src
+ ($prompt escape? tag handler)))))))))
+
+ (intset-fold (lambda (label cps)
+ (match (intmap-ref cps label (lambda (_) #f))
+ (($ $kargs names vars term)
+ (with-cps cps
+ (let$ term (visit-term term))
+ (setk label ($kargs names vars ,term))))
+ (_ cps)))
+ body
+ cps)))
+
+(define (convert-closures cps)
+ "Convert free reference in @var{cps} to primcalls to @code{free-ref},
+and allocate and initialize flat closures."
+ (let* ((kfun 0) ;; Ass-u-me.
+ ;; label -> body-label...
+ (functions (compute-function-bodies cps kfun))
+ (cps (filter-reachable cps functions))
+ ;; label -> bound-var...
+ (label->bound (compute-function-names cps functions))
+ ;; bound-var -> label
+ (bound->label (invert-partition label->bound))
+ ;; label...
+ (well-known (compute-well-known-functions cps bound->label))
+ ;; label -> closure-var
+ (shared (compute-shared-closures cps well-known))
+ (cps (rewrite-shared-closure-calls cps functions label->bound shared
+ kfun))
+ ;; label -> free-var...
+ (free-vars (compute-free-vars cps kfun shared))
+ (free-vars (prune-free-vars free-vars bound->label well-known shared)))
+ (let ((free-in-program (intmap-ref free-vars kfun)))
+ (unless (eq? empty-intset free-in-program)
+ (error "Expected no free vars in program" free-in-program)))
+ (with-fresh-name-state cps
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label body cps)
+ (convert-one cps label body free-vars bound->label well-known shared))
+ functions
+ cps)))))
+
+;;; Local Variables:
+;;; eval: (put 'convert-arg 'scheme-indent-function 2)
+;;; eval: (put 'convert-args 'scheme-indent-function 2)
+;;; End:
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
new file mode 100644
index 000000000..61f1e0781
--- /dev/null
+++ b/module/language/cps/compile-bytecode.scm
@@ -0,0 +1,433 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Compiling CPS to bytecode. The result is in the bytecode language,
+;;; which happens to be an ELF image as a bytecode.
+;;;
+;;; Code:
+
+(define-module (language cps compile-bytecode)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (language cps)
+ #:use-module (language cps primitives)
+ #:use-module (language cps slot-allocation)
+ #:use-module (language cps utils)
+ #:use-module (language cps closure-conversion)
+ #:use-module (language cps optimize)
+ #:use-module (language cps reify-primitives)
+ #:use-module (language cps renumber)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:use-module (system vm assembler)
+ #:export (compile-bytecode))
+
+(define (kw-arg-ref args kw default)
+ (match (memq kw args)
+ ((_ val . _) val)
+ (_ default)))
+
+(define (intmap-for-each f map)
+ (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
+
+(define (intmap-select map set)
+ (persistent-intmap
+ (intset-fold
+ (lambda (k out)
+ (intmap-add! out k (intmap-ref map k)))
+ set
+ empty-intmap)))
+
+(define (compile-function cps asm)
+ (let ((allocation (allocate-slots cps))
+ (frame-size #f))
+ (define (maybe-slot sym)
+ (lookup-maybe-slot sym allocation))
+
+ (define (slot sym)
+ (lookup-slot sym allocation))
+
+ (define (constant sym)
+ (lookup-constant-value sym allocation))
+
+ (define (maybe-mov dst src)
+ (unless (= dst src)
+ (emit-mov asm dst src)))
+
+ (define (compile-tail label exp)
+ ;; There are only three kinds of expressions in tail position:
+ ;; tail calls, multiple-value returns, and single-value returns.
+ (match exp
+ (($ $call proc args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-tail-call asm (1+ (length args))))
+ (($ $callk k proc args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-tail-call-label asm (1+ (length args)) k))
+ (($ $values ())
+ (emit-reset-frame asm 1)
+ (emit-return-values asm))
+ (($ $values (arg))
+ (if (maybe-slot arg)
+ (emit-return asm (slot arg))
+ (begin
+ (emit-load-constant asm 1 (constant arg))
+ (emit-return asm 1))))
+ (($ $values args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-reset-frame asm (1+ (length args)))
+ (emit-return-values asm))
+ (($ $primcall 'return (arg))
+ (emit-return asm (slot arg)))))
+
+ (define (compile-value label exp dst)
+ (match exp
+ (($ $values (arg))
+ (maybe-mov dst (slot arg)))
+ (($ $const exp)
+ (emit-load-constant asm dst exp))
+ (($ $closure k 0)
+ (emit-load-static-procedure asm dst k))
+ (($ $closure k nfree)
+ (emit-make-closure asm dst k nfree))
+ (($ $primcall 'current-module)
+ (emit-current-module asm dst))
+ (($ $primcall 'cached-toplevel-box (scope name bound?))
+ (emit-cached-toplevel-box asm dst (constant scope) (constant name)
+ (constant bound?)))
+ (($ $primcall 'cached-module-box (mod name public? bound?))
+ (emit-cached-module-box asm dst (constant mod) (constant name)
+ (constant public?) (constant bound?)))
+ (($ $primcall 'resolve (name bound?))
+ (emit-resolve asm dst (constant bound?) (slot name)))
+ (($ $primcall 'free-ref (closure idx))
+ (emit-free-ref asm dst (slot closure) (constant idx)))
+ (($ $primcall 'vector-ref (vector index))
+ (emit-vector-ref asm dst (slot vector) (slot index)))
+ (($ $primcall 'make-vector (length init))
+ (emit-make-vector asm dst (slot length) (slot init)))
+ (($ $primcall 'make-vector/immediate (length init))
+ (emit-make-vector/immediate asm dst (constant length) (slot init)))
+ (($ $primcall 'vector-ref/immediate (vector index))
+ (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
+ (($ $primcall 'allocate-struct (vtable nfields))
+ (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
+ (($ $primcall 'allocate-struct/immediate (vtable nfields))
+ (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
+ (($ $primcall 'struct-ref (struct n))
+ (emit-struct-ref asm dst (slot struct) (slot n)))
+ (($ $primcall 'struct-ref/immediate (struct n))
+ (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
+ (($ $primcall 'builtin-ref (name))
+ (emit-builtin-ref asm dst (constant name)))
+ (($ $primcall 'bv-u8-ref (bv idx))
+ (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s8-ref (bv idx))
+ (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u16-ref (bv idx))
+ (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s16-ref (bv idx))
+ (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u32-ref (bv idx val))
+ (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s32-ref (bv idx val))
+ (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-u64-ref (bv idx val))
+ (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-s64-ref (bv idx val))
+ (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-f32-ref (bv idx val))
+ (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall 'bv-f64-ref (bv idx val))
+ (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
+ (($ $primcall name args)
+ ;; FIXME: Inline all the cases.
+ (let ((inst (prim-instruction name)))
+ (emit-text asm `((,inst ,dst ,@(map slot args))))))))
+
+ (define (compile-effect label exp k)
+ (match exp
+ (($ $values ()) #f)
+ (($ $prompt escape? tag handler)
+ (match (intmap-ref cps handler)
+ (($ $kreceive ($ $arity req () rest () #f) khandler-body)
+ (let ((receive-args (gensym "handler"))
+ (nreq (length req))
+ (proc-slot (lookup-call-proc-slot label allocation)))
+ (emit-prompt asm (slot tag) escape? proc-slot receive-args)
+ (emit-br asm k)
+ (emit-label asm receive-args)
+ (unless (and rest (zero? nreq))
+ (emit-receive-values asm proc-slot (->bool rest) nreq))
+ (when (and rest
+ (match (intmap-ref cps khandler-body)
+ (($ $kargs names (_ ... rest))
+ (maybe-slot rest))))
+ (emit-bind-rest asm (+ proc-slot 1 nreq)))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves handler allocation))
+ (emit-reset-frame asm frame-size)
+ (emit-br asm khandler-body)))))
+ (($ $primcall 'cache-current-module! (sym scope))
+ (emit-cache-current-module! asm (slot sym) (constant scope)))
+ (($ $primcall 'free-set! (closure idx value))
+ (emit-free-set! asm (slot closure) (slot value) (constant idx)))
+ (($ $primcall 'box-set! (box value))
+ (emit-box-set! asm (slot box) (slot value)))
+ (($ $primcall 'struct-set! (struct index value))
+ (emit-struct-set! asm (slot struct) (slot index) (slot value)))
+ (($ $primcall 'struct-set!/immediate (struct index value))
+ (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
+ (($ $primcall 'vector-set! (vector index value))
+ (emit-vector-set! asm (slot vector) (slot index) (slot value)))
+ (($ $primcall 'vector-set!/immediate (vector index value))
+ (emit-vector-set!/immediate asm (slot vector) (constant index)
+ (slot value)))
+ (($ $primcall 'set-car! (pair value))
+ (emit-set-car! asm (slot pair) (slot value)))
+ (($ $primcall 'set-cdr! (pair value))
+ (emit-set-cdr! asm (slot pair) (slot value)))
+ (($ $primcall 'define! (sym value))
+ (emit-define! asm (slot sym) (slot value)))
+ (($ $primcall 'push-fluid (fluid val))
+ (emit-push-fluid asm (slot fluid) (slot val)))
+ (($ $primcall 'pop-fluid ())
+ (emit-pop-fluid asm))
+ (($ $primcall 'wind (winder unwinder))
+ (emit-wind asm (slot winder) (slot unwinder)))
+ (($ $primcall 'bv-u8-set! (bv idx val))
+ (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s8-set! (bv idx val))
+ (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u16-set! (bv idx val))
+ (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s16-set! (bv idx val))
+ (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u32-set! (bv idx val))
+ (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s32-set! (bv idx val))
+ (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-u64-set! (bv idx val))
+ (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-s64-set! (bv idx val))
+ (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-f32-set! (bv idx val))
+ (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'bv-f64-set! (bv idx val))
+ (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
+ (($ $primcall 'unwind ())
+ (emit-unwind asm))))
+
+ (define (compile-values label exp syms)
+ (match exp
+ (($ $values args)
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation)))))
+
+ (define (compile-test label exp kt kf next-label)
+ (define (unary op sym)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot sym) #t kf))
+ (else
+ (op asm (slot sym) #f kt)
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))))
+ (define (binary op a b)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot a) (slot b) #t kf))
+ (else
+ (op asm (slot a) (slot b) #f kt)
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))))
+ (match exp
+ (($ $values (sym))
+ (call-with-values (lambda ()
+ (lookup-maybe-constant-value sym allocation))
+ (lambda (has-const? val)
+ (if has-const?
+ (if val
+ (unless (eq? kt next-label)
+ (emit-br asm kt))
+ (unless (eq? kf next-label)
+ (emit-br asm kf)))
+ (unary emit-br-if-true sym)))))
+ (($ $primcall 'null? (a)) (unary emit-br-if-null a))
+ (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
+ (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
+ (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
+ (($ $primcall 'char? (a)) (unary emit-br-if-char a))
+ (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
+ (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
+ (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
+ (($ $primcall 'string? (a)) (unary emit-br-if-string a))
+ (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
+ (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
+ (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
+ ;; Add more TC7 tests here. Keep in sync with
+ ;; *branching-primcall-arities* in (language cps primitives) and
+ ;; the set of macro-instructions in assembly.scm.
+ (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
+ (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
+ (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
+ (($ $primcall '< (a b)) (binary emit-br-if-< a b))
+ (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
+ (($ $primcall '= (a b)) (binary emit-br-if-= a b))
+ (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
+ (($ $primcall '> (a b)) (binary emit-br-if-< b a))
+ (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
+
+ (define (compile-trunc label k exp nreq rest-var)
+ (define (do-call proc args emit-call)
+ (let* ((proc-slot (lookup-call-proc-slot label allocation))
+ (nargs (1+ (length args)))
+ (arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves label allocation))
+ (emit-call asm proc-slot nargs)
+ (emit-dead-slot-map asm proc-slot
+ (lookup-dead-slot-map label allocation))
+ (cond
+ ((and (= 1 nreq) (and rest-var) (not (maybe-slot rest-var))
+ (match (lookup-parallel-moves k allocation)
+ ((((? (lambda (src) (= src (1+ proc-slot))) src)
+ . dst)) dst)
+ (_ #f)))
+ ;; The usual case: one required live return value, ignoring
+ ;; any additional values.
+ => (lambda (dst)
+ (emit-receive asm dst proc-slot frame-size)))
+ (else
+ (unless (and (zero? nreq) rest-var)
+ (emit-receive-values asm proc-slot (->bool rest-var) nreq))
+ (when (and rest-var (maybe-slot rest-var))
+ (emit-bind-rest asm (+ proc-slot 1 nreq)))
+ (for-each (match-lambda
+ ((src . dst) (emit-mov asm dst src)))
+ (lookup-parallel-moves k allocation))
+ (emit-reset-frame asm frame-size)))))
+ (match exp
+ (($ $call proc args)
+ (do-call proc args
+ (lambda (asm proc-slot nargs)
+ (emit-call asm proc-slot nargs))))
+ (($ $callk k proc args)
+ (do-call proc args
+ (lambda (asm proc-slot nargs)
+ (emit-call-label asm proc-slot nargs k))))))
+
+ (define (compile-expression label k exp)
+ (let* ((fallthrough? (= k (1+ label))))
+ (define (maybe-emit-jump)
+ (unless fallthrough?
+ (emit-br asm k)))
+ (match (intmap-ref cps k)
+ (($ $ktail)
+ (compile-tail label exp))
+ (($ $kargs (name) (sym))
+ (let ((dst (maybe-slot sym)))
+ (when dst
+ (compile-value label exp dst)))
+ (maybe-emit-jump))
+ (($ $kargs () ())
+ (match exp
+ (($ $branch kt exp)
+ (compile-test label exp kt k (1+ label)))
+ (_
+ (compile-effect label exp k)
+ (maybe-emit-jump))))
+ (($ $kargs names syms)
+ (compile-values label exp syms)
+ (maybe-emit-jump))
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (compile-trunc label k exp (length req)
+ (and rest
+ (match (intmap-ref cps kargs)
+ (($ $kargs names (_ ... rest)) rest))))
+ (unless (and fallthrough? (= kargs (1+ k)))
+ (emit-br asm kargs))))))
+
+ (define (compile-cont label cont)
+ (match cont
+ (($ $kfun src meta self tail clause)
+ (when src
+ (emit-source asm src))
+ (emit-begin-program asm label meta))
+ (($ $kclause ($ $arity req opt rest kw allow-other-keys?) body alt)
+ (let ((first? (match (intmap-ref cps (1- label))
+ (($ $kfun) #t)
+ (_ #f)))
+ (kw-indices (map (match-lambda
+ ((key name sym)
+ (cons key (lookup-slot sym allocation))))
+ kw)))
+ (unless first?
+ (emit-end-arity asm))
+ (emit-label asm label)
+ (set! frame-size (lookup-nlocals label allocation))
+ (emit-begin-kw-arity asm req opt rest kw-indices allow-other-keys?
+ frame-size alt)))
+ (($ $kargs names vars ($ $continue k src exp))
+ (emit-label asm label)
+ (for-each (lambda (name var)
+ (let ((slot (maybe-slot var)))
+ (when slot
+ (emit-definition asm name slot))))
+ names vars)
+ (when src
+ (emit-source asm src))
+ (compile-expression label k exp))
+ (($ $kreceive arity kargs)
+ (emit-label asm label))
+ (($ $ktail)
+ (emit-end-arity asm)
+ (emit-end-program asm))))
+
+ (intmap-for-each compile-cont cps)))
+
+(define (emit-bytecode exp env opts)
+ (let ((asm (make-assembler)))
+ (intmap-for-each (lambda (kfun body)
+ (compile-function (intmap-select exp body) asm))
+ (compute-reachable-functions exp 0))
+ (values (link-assembly asm #:page-aligned? (kw-arg-ref opts #:to-file? #f))
+ env
+ env)))
+
+(define (lower-cps exp opts)
+ (set! exp (optimize-higher-order-cps exp opts))
+ (set! exp (convert-closures exp))
+ (set! exp (optimize-first-order-cps exp opts))
+ (set! exp (reify-primitives exp))
+ (renumber exp))
+
+(define (compile-bytecode exp env opts)
+ (set! exp (lower-cps exp opts))
+ (emit-bytecode exp env opts))
diff --git a/module/language/cps/constructors.scm b/module/language/cps/constructors.scm
new file mode 100644
index 000000000..f86095198
--- /dev/null
+++ b/module/language/cps/constructors.scm
@@ -0,0 +1,98 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Constructor inlining turns "list" primcalls into a series of conses,
+;;; and does similar transformations for "vector".
+;;;
+;;; Code:
+
+(define-module (language cps constructors)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:export (inline-constructors))
+
+(define (inline-list out k src args)
+ (define (build-list out args k)
+ (match args
+ (()
+ (with-cps out
+ (build-term ($continue k src ($const '())))))
+ ((arg . args)
+ (with-cps out
+ (letv tail)
+ (letk ktail ($kargs ('tail) (tail)
+ ($continue k src
+ ($primcall 'cons (arg tail)))))
+ ($ (build-list args ktail))))))
+ (with-cps out
+ (letv val)
+ (letk kvalues ($kargs ('val) (val)
+ ($continue k src
+ ($primcall 'values (val)))))
+ ($ (build-list args kvalues))))
+
+(define (inline-vector out k src args)
+ (define (initialize out vec args n)
+ (match args
+ (()
+ (with-cps out
+ (build-term ($continue k src ($primcall 'values (vec))))))
+ ((arg . args)
+ (with-cps out
+ (let$ next (initialize vec args (1+ n)))
+ (letk knext ($kargs () () ,next))
+ ($ (with-cps-constants ((idx n))
+ (build-term ($continue knext src
+ ($primcall 'vector-set! (vec idx arg))))))))))
+ (with-cps out
+ (letv vec)
+ (let$ body (initialize vec args 0))
+ (letk kalloc ($kargs ('vec) (vec) ,body))
+ ($ (with-cps-constants ((len (length args))
+ (init #f))
+ (build-term ($continue kalloc src
+ ($primcall 'make-vector (len init))))))))
+
+(define (find-constructor-inliner name)
+ (match name
+ ('list inline-list)
+ ('vector inline-vector)
+ (_ #f)))
+
+(define (inline-constructors conts)
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
+ (let ((inline (find-constructor-inliner name)))
+ (if inline
+ (call-with-values (lambda () (inline out k src args))
+ (lambda (out term)
+ (intmap-replace! out label
+ (build-cont ($kargs names vars ,term)))))
+ out)))
+ (_ out)))
+ conts
+ conts))))
diff --git a/module/language/cps/contification.scm b/module/language/cps/contification.scm
new file mode 100644
index 000000000..4a398d7e5
--- /dev/null
+++ b/module/language/cps/contification.scm
@@ -0,0 +1,475 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Contification is a pass that turns $fun instances into $cont
+;;; instances if all calls to the $fun return to the same continuation.
+;;; This is a more rigorous variant of our old "fixpoint labels
+;;; allocation" optimization.
+;;;
+;;; See Kennedy's "Compiling with Continuations, Continued", and Fluet
+;;; and Weeks's "Contification using Dominators".
+;;;
+;;; Code:
+
+(define-module (language cps contification)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-11)
+ #:use-module ((srfi srfi-1) #:select (fold))
+ #:use-module (language cps)
+ #:use-module (language cps renumber)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (contify))
+
+(define (compute-singly-referenced-labels conts)
+ "Compute the set of labels in CONTS that have exactly one
+predecessor."
+ (define (add-ref label cont single multiple)
+ (define (ref k single multiple)
+ (if (intset-ref single k)
+ (values single (intset-add! multiple k))
+ (values (intset-add! single k) multiple)))
+ (define (ref0) (values single multiple))
+ (define (ref1 k) (ref k single multiple))
+ (define (ref2 k k*)
+ (if k*
+ (let-values (((single multiple) (ref k single multiple)))
+ (ref k* single multiple))
+ (ref1 k)))
+ (match cont
+ (($ $kreceive arity k) (ref1 k))
+ (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
+ (($ $ktail) (ref0))
+ (($ $kclause arity kbody kalt) (ref2 kbody kalt))
+ (($ $kargs names syms ($ $continue k src exp))
+ (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
+ (let*-values (((single multiple) (values empty-intset empty-intset))
+ ((single multiple) (intmap-fold add-ref conts single multiple)))
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple))))
+
+(define (compute-functions conts)
+ "Compute a map from $kfun label to bound variable names for all
+functions in CONTS. Functions have two bound variable names: their self
+binding, and the name they are given in their continuation. If their
+continuation has more than one predecessor, then the bound variable name
+doesn't uniquely identify the function, so we exclude that function from
+the set."
+ (define (function-self label)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self) self)))
+ (let ((single (compute-singly-referenced-labels conts)))
+ (intmap-fold (lambda (label cont functions)
+ (match cont
+ (($ $kargs _ _ ($ $continue k src ($ $fun kfun)))
+ (if (intset-ref single k)
+ (match (intmap-ref conts k)
+ (($ $kargs (name) (var))
+ (intmap-add functions kfun
+ (intset var (function-self kfun)))))
+ functions))
+ (($ $kargs _ _ ($ $continue k src
+ ($ $rec _ vars (($ $fun kfuns) ...))))
+ (if (intset-ref single k)
+ (fold (lambda (var kfun functions)
+ (intmap-add functions kfun
+ (intset var (function-self kfun))))
+ functions vars kfuns)
+ functions))
+ (_ functions)))
+ conts
+ empty-intmap)))
+
+(define (compute-multi-clause conts)
+ "Compute an set containing all labels that are part of a multi-clause
+case-lambda. See the note in compute-contification-candidates."
+ (define (multi-clause? clause)
+ (and clause
+ (match (intmap-ref conts clause)
+ (($ $kclause arity body alt)
+ alt))))
+ (intmap-fold (lambda (label cont multi)
+ (match cont
+ (($ $kfun src meta self tail clause)
+ (if (multi-clause? clause)
+ (intset-union multi (compute-function-body conts label))
+ multi))
+ (_ multi)))
+ conts
+ empty-intset))
+
+(define (compute-arities conts functions)
+ "Given the map FUNCTIONS whose keys are $kfun labels, return a map
+from label to arities."
+ (define (clause-arities clause)
+ (if clause
+ (match (intmap-ref conts clause)
+ (($ $kclause arity body alt)
+ (cons arity (clause-arities alt))))
+ '()))
+ (intmap-map (lambda (label vars)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail clause)
+ (clause-arities clause))))
+ functions))
+
+;; For now, we don't contify functions with optional, keyword, or rest
+;; arguments.
+(define (contifiable-arity? arity)
+ (match arity
+ (($ $arity req () #f () aok?)
+ #t)
+ (_
+ #f)))
+
+(define (arity-matches? arity nargs)
+ (match arity
+ (($ $arity req () #f () aok?)
+ (= nargs (length req)))
+ (_
+ #f)))
+
+(define (compute-contification-candidates conts)
+ "Compute and return a label -> (variable ...) map describing all
+functions with known uses that are only ever used as the operator of a
+$call, and are always called with a compatible arity."
+ (let* ((functions (compute-functions conts))
+ (multi-clause (compute-multi-clause conts))
+ (vars (intmap-fold (lambda (label vars out)
+ (intset-fold (lambda (var out)
+ (intmap-add out var label))
+ vars out))
+ functions
+ empty-intmap))
+ (arities (compute-arities conts functions)))
+ (define (restrict-arity functions proc nargs)
+ (match (intmap-ref vars proc (lambda (_) #f))
+ (#f functions)
+ (label
+ (let lp ((arities (intmap-ref arities label)))
+ (match arities
+ (() (intmap-remove functions label))
+ ((arity . arities)
+ (cond
+ ((not (contifiable-arity? arity)) (lp '()))
+ ((arity-matches? arity nargs) functions)
+ (else (lp arities)))))))))
+ (define (visit-cont label cont functions)
+ (define (exclude-var functions var)
+ (match (intmap-ref vars var (lambda (_) #f))
+ (#f functions)
+ (label (intmap-remove functions label))))
+ (define (exclude-vars functions vars)
+ (match vars
+ (() functions)
+ ((var . vars)
+ (exclude-vars (exclude-var functions var) vars))))
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $closure) ($ $fun) ($ $rec))
+ functions)
+ (($ $values args)
+ (exclude-vars functions args))
+ (($ $call proc args)
+ (let ((functions (exclude-vars functions args)))
+ ;; This contification algorithm is happy to contify the
+ ;; `lp' in this example into a shared tail between clauses:
+ ;;
+ ;; (letrec ((lp (lambda () (lp))))
+ ;; (case-lambda
+ ;; ((a) (lp))
+ ;; ((a b) (lp))))
+ ;;
+ ;; However because the current compilation pipeline has to
+ ;; re-nest continuations into old CPS, there would be no
+ ;; scope in which the tail would be valid. So, until the
+ ;; old compilation pipeline is completely replaced,
+ ;; conservatively exclude contifiable fucntions called
+ ;; from multi-clause procedures.
+ (if (intset-ref multi-clause label)
+ (exclude-var functions proc)
+ (restrict-arity functions proc (length args)))))
+ (($ $callk k proc args)
+ (exclude-vars functions (cons proc args)))
+ (($ $branch kt ($ $primcall name args))
+ (exclude-vars functions args))
+ (($ $branch kt ($ $values (arg)))
+ (exclude-var functions arg))
+ (($ $primcall name args)
+ (exclude-vars functions args))
+ (($ $prompt escape? tag handler)
+ (exclude-var functions tag))))
+ (_ functions)))
+ (intmap-fold visit-cont conts functions)))
+
+(define (compute-call-graph conts labels vars)
+ "Given the set of contifiable functions LABELS and associated bound
+variables VARS, compute and return two values: a map
+LABEL->LABEL... indicating the contifiable functions called by a
+function, and a map LABEL->LABEL... indicating the return continuations
+for a function. The first return value also has an entry
+0->LABEL... indicating all contifiable functions called by
+non-contifiable functions. We assume that 0 is not in the contifiable
+function set."
+ (let ((bodies
+ ;; label -> fun-label for all labels in bodies of contifiable
+ ;; functions
+ (intset-fold (lambda (fun-label bodies)
+ (intset-fold (lambda (label bodies)
+ (intmap-add bodies label fun-label))
+ (compute-function-body conts fun-label)
+ bodies))
+ labels
+ empty-intmap)))
+ (when (intset-ref labels 0)
+ (error "internal error: label 0 should not be contifiable"))
+ (intmap-fold
+ (lambda (label cont calls returns)
+ (match cont
+ (($ $kargs _ _ ($ $continue k src ($ $call proc)))
+ (match (intmap-ref vars proc (lambda (_) #f))
+ (#f (values calls returns))
+ (callee
+ (let ((caller (intmap-ref bodies label (lambda (_) 0))))
+ (values (intmap-add calls caller callee intset-add)
+ (intmap-add returns callee k intset-add))))))
+ (_ (values calls returns))))
+ conts
+ (intset->intmap (lambda (label) empty-intset) (intset-add labels 0))
+ (intset->intmap (lambda (label) empty-intset) labels))))
+
+(define (tail-label conts label)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail body)
+ tail)))
+
+(define (compute-return-labels labels tails returns return-substs)
+ (define (subst k)
+ (match (intmap-ref return-substs k (lambda (_) #f))
+ (#f k)
+ (k (subst k))))
+ ;; Compute all return labels, then subtract tail labels of the
+ ;; functions in question.
+ (intset-subtract
+ ;; Return labels for all calls to these labels.
+ (intset-fold (lambda (label out)
+ (intset-fold (lambda (k out)
+ (intset-add out (subst k)))
+ (intmap-ref returns label)
+ out))
+ labels
+ empty-intset)
+ (intset-fold (lambda (label out)
+ (intset-add out (intmap-ref tails label)))
+ labels
+ empty-intset)))
+
+(define (intmap->intset map)
+ (define (add-key label cont labels)
+ (intset-add labels label))
+ (intmap-fold add-key map empty-intset))
+
+(define (filter-contifiable contified groups)
+ (intmap-fold (lambda (id labels groups)
+ (let ((labels (intset-subtract labels contified)))
+ (if (eq? empty-intset labels)
+ groups
+ (intmap-add groups id labels))))
+ groups
+ empty-intmap))
+
+(define (trivial-set set)
+ (let ((first (intset-next set)))
+ (and first
+ (not (intset-next set (1+ first)))
+ first)))
+
+(define (compute-contification conts)
+ (let*-values
+ (;; label -> (var ...)
+ ((candidates) (compute-contification-candidates conts))
+ ((labels) (intmap->intset candidates))
+ ;; var -> label
+ ((vars) (intmap-fold (lambda (label vars out)
+ (intset-fold (lambda (var out)
+ (intmap-add out var label))
+ vars out))
+ candidates
+ empty-intmap))
+ ;; caller-label -> callee-label..., callee-label -> return-label...
+ ((calls returns) (compute-call-graph conts labels vars))
+ ;; callee-label -> tail-label
+ ((tails) (intset-fold
+ (lambda (label tails)
+ (intmap-add tails label (tail-label conts label)))
+ labels
+ empty-intmap))
+ ;; Strongly connected components, allowing us to contify mutually
+ ;; tail-recursive functions. Since `compute-call-graph' added on
+ ;; a synthetic 0->LABEL... entry for contifiable functions called
+ ;; by non-contifiable functions, we need to remove that entry
+ ;; from the partition. It will be in its own component, as it
+ ;; has no predecessors.
+ ;;
+ ;; id -> label...
+ ((groups) (intmap-remove
+ (compute-strongly-connected-components calls 0)
+ 0)))
+ ;; todo: thread groups through contification
+ (define (attempt-contification labels contified return-substs)
+ (let ((returns (compute-return-labels labels tails returns
+ return-substs)))
+ (cond
+ ((trivial-set returns)
+ => (lambda (k)
+ ;; Success!
+ (values (intset-union contified labels)
+ (intset-fold (lambda (label return-substs)
+ (let ((tail (intmap-ref tails label)))
+ (intmap-add return-substs tail k)))
+ labels return-substs))))
+ ((trivial-set labels)
+ ;; Single-label SCC failed to contify.
+ (values contified return-substs))
+ (else
+ ;; Multi-label SCC failed to contify. Try instead to contify
+ ;; each one.
+ (intset-fold
+ (lambda (label contified return-substs)
+ (let ((labels (intset-add empty-intset label)))
+ (attempt-contification labels contified return-substs)))
+ labels contified return-substs)))))
+ (call-with-values
+ (lambda ()
+ (fixpoint
+ (lambda (contified return-substs)
+ (intmap-fold
+ (lambda (id group contified return-substs)
+ (attempt-contification group contified return-substs))
+ (filter-contifiable contified groups)
+ contified
+ return-substs))
+ empty-intset
+ empty-intmap))
+ (lambda (contified return-substs)
+ (values (intset-fold (lambda (label call-substs)
+ (intset-fold
+ (lambda (var call-substs)
+ (intmap-add call-substs var label))
+ (intmap-ref candidates label)
+ call-substs))
+ contified
+ empty-intmap)
+ return-substs)))))
+
+(define (apply-contification conts call-substs return-substs)
+ (define (call-subst proc)
+ (intmap-ref call-substs proc (lambda (_) #f)))
+ (define (return-subst k)
+ (intmap-ref return-substs k (lambda (_) #f)))
+ (define (find-body kfun nargs)
+ (match (intmap-ref conts kfun)
+ (($ $kfun src meta self tail clause)
+ (let lp ((clause clause))
+ (match (intmap-ref conts clause)
+ (($ $kclause arity body alt)
+ (if (arity-matches? arity nargs)
+ body
+ (lp alt))))))))
+ (define (continue k src exp)
+ (define (lookup-return-cont k)
+ (match (return-subst k)
+ (#f k)
+ (k (lookup-return-cont k))))
+ (let ((k* (lookup-return-cont k)))
+ (if (eq? k k*)
+ (build-term ($continue k src ,exp))
+ ;; We are contifying this return. It must be a call, a
+ ;; $values expression, or a return primcall. k* will be
+ ;; either a $ktail or a $kreceive continuation. CPS has this
+ ;; thing though where $kreceive can't be the target of a
+ ;; $values expression, and "return" can only continue to a
+ ;; tail continuation, so we might have to rewrite to a
+ ;; "values" primcall.
+ (build-term
+ ($continue k* src
+ ,(match (intmap-ref conts k*)
+ (($ $kreceive)
+ (match exp
+ (($ $primcall 'return (val))
+ (build-exp ($primcall 'values (val))))
+ (($ $call) exp)
+ ;; Except for 'return, a primcall that can continue
+ ;; to $ktail can also continue to $kreceive. TODO:
+ ;; replace 'return with 'values, for consistency.
+ (($ $primcall) exp)
+ (($ $values vals)
+ (build-exp ($primcall 'values vals)))))
+ (($ $ktail) exp)))))))
+ (define (visit-exp k src exp)
+ (match exp
+ (($ $call proc args)
+ ;; If proc is contifiable, replace call with jump.
+ (match (call-subst proc)
+ (#f (continue k src exp))
+ (kfun
+ (let ((body (find-body kfun (length args))))
+ (build-term ($continue body src ($values args)))))))
+ (($ $fun kfun)
+ ;; If the function's tail continuation has been
+ ;; substituted, that means it has been contified.
+ (if (return-subst (tail-label conts kfun))
+ (continue k src (build-exp ($values ())))
+ (continue k src exp)))
+ (($ $rec names vars funs)
+ (match (filter (match-lambda ((n v f) (not (call-subst v))))
+ (map list names vars funs))
+ (() (continue k src (build-exp ($values ()))))
+ (((names vars funs) ...)
+ (continue k src (build-exp ($rec names vars funs))))))
+ (_ (continue k src exp))))
+
+ ;; Renumbering is not strictly necessary but some passes may not be
+ ;; equipped to deal with stale $kfun nodes whose bodies have been
+ ;; wired into other functions.
+ (renumber
+ (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ ;; Remove bindings for functions that have been contified.
+ (match (filter (match-lambda ((name var) (not (call-subst var))))
+ (map list names vars))
+ (((names vars) ...)
+ (build-cont
+ ($kargs names vars ,(visit-exp k src exp))))))
+ (_ cont)))
+ conts)))
+
+(define (contify conts)
+ ;; FIXME: Renumbering isn't really needed but dead continuations may
+ ;; cause compute-singly-referenced-labels to spuriously mark some
+ ;; conts as irreducible. For now we punt and renumber so that there
+ ;; are only live conts.
+ (let ((conts (renumber conts)))
+ (let-values (((call-substs return-substs) (compute-contification conts)))
+ (apply-contification conts call-substs return-substs))))
diff --git a/module/language/cps/cse.scm b/module/language/cps/cse.scm
new file mode 100644
index 000000000..def542063
--- /dev/null
+++ b/module/language/cps/cse.scm
@@ -0,0 +1,449 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Common subexpression elimination for CPS.
+;;;
+;;; Code:
+
+(define-module (language cps cse)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps effects-analysis)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (eliminate-common-subexpressions))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define-syntax-rule (make-worklist-folder* seed ...)
+ (lambda (f worklist seed ...)
+ (let lp ((worklist worklist) (seed seed) ...)
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist i)
+ (if i
+ (call-with-values (lambda () (f i seed ...))
+ (lambda (i* seed ...)
+ (let add ((i* i*) (worklist worklist))
+ (match i*
+ (() (lp worklist seed ...))
+ ((i . i*) (add i* (intset-add worklist i)))))))
+ (values seed ...)))))))
+
+(define worklist-fold*
+ (case-lambda
+ ((f worklist seed)
+ ((make-worklist-folder* seed) f worklist seed))))
+
+(define (compute-available-expressions conts kfun effects)
+ "Compute and return a map of LABEL->ANCESTOR..., where ANCESTOR... is
+an intset containing ancestor labels whose value is available at LABEL."
+ (define (propagate avail succ out)
+ (let* ((in (intmap-ref avail succ (lambda (_) #f)))
+ (in* (if in (intset-intersect in out) out)))
+ (if (eq? in in*)
+ (values '() avail)
+ (values (list succ)
+ (intmap-add avail succ in* (lambda (old new) new))))))
+
+ (define (clobber label in)
+ (let ((fx (intmap-ref effects label)))
+ (cond
+ ((not (causes-effect? fx &write))
+ ;; Fast-path if this expression clobbers nothing.
+ in)
+ (else
+ ;; Kill clobbered expressions. FIXME: there is no need to check
+ ;; on any label before than the last dominating label that
+ ;; clobbered everything. Another way to speed things up would
+ ;; be to compute a clobber set per-effect, which we could
+ ;; subtract from "in".
+ (let lp ((label 0) (in in))
+ (cond
+ ((intset-next in label)
+ => (lambda (label)
+ (if (effect-clobbers? fx (intmap-ref effects label))
+ (lp (1+ label) (intset-remove in label))
+ (lp (1+ label) in))))
+ (else in)))))))
+
+ (define (visit-cont label avail)
+ (let* ((in (intmap-ref avail label))
+ (out (intset-add (clobber label in) label)))
+ (define (propagate0)
+ (values '() avail))
+ (define (propagate1 succ)
+ (propagate avail succ out))
+ (define (propagate2 succ0 succ1)
+ (let*-values (((changed0 avail) (propagate avail succ0 out))
+ ((changed1 avail) (propagate avail succ1 out)))
+ (values (append changed0 changed1) avail)))
+
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $branch kt) (propagate2 k kt))
+ (($ $prompt escape? tag handler) (propagate2 k handler))
+ (_ (propagate1 k))))
+ (($ $kreceive arity k)
+ (propagate1 k))
+ (($ $kfun src meta self tail clause)
+ (if clause
+ (propagate1 clause)
+ (propagate0)))
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (propagate2 kbody kalt)
+ (propagate1 kbody)))
+ (($ $ktail) (propagate0)))))
+
+ (worklist-fold* visit-cont
+ (intset kfun)
+ (intmap-add empty-intmap kfun empty-intset)))
+
+(define (compute-truthy-expressions conts kfun boolv)
+ "Compute a \"truth map\", indicating which expressions can be shown to
+be true and/or false at each label in the function starting at KFUN..
+Returns an intmap of intsets. The even elements of the intset indicate
+labels that may be true, and the odd ones indicate those that may be
+false. It could be that both true and false proofs are available."
+ (define (true-idx label) (ash label 1))
+ (define (false-idx label) (1+ (ash label 1)))
+
+ (define (propagate boolv succ out)
+ (let* ((in (intmap-ref boolv succ (lambda (_) #f)))
+ (in* (if in (intset-intersect in out) out)))
+ (if (eq? in in*)
+ (values '() boolv)
+ (values (list succ)
+ (intmap-add boolv succ in* (lambda (old new) new))))))
+
+ (define (visit-cont label boolv)
+ (let ((in (intmap-ref boolv label)))
+ (define (propagate0)
+ (values '() boolv))
+ (define (propagate1 succ)
+ (propagate boolv succ in))
+ (define (propagate2 succ0 succ1)
+ (let*-values (((changed0 boolv) (propagate boolv succ0 in))
+ ((changed1 boolv) (propagate boolv succ1 in)))
+ (values (append changed0 changed1) boolv)))
+ (define (propagate-branch succ0 succ1)
+ (let*-values (((changed0 boolv)
+ (propagate boolv succ0
+ (intset-add in (false-idx label))))
+ ((changed1 boolv)
+ (propagate boolv succ1
+ (intset-add in (true-idx label)))))
+ (values (append changed0 changed1) boolv)))
+
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $branch kt) (propagate-branch k kt))
+ (($ $prompt escape? tag handler) (propagate2 k handler))
+ (_ (propagate1 k))))
+ (($ $kreceive arity k)
+ (propagate1 k))
+ (($ $kfun src meta self tail clause)
+ (if clause
+ (propagate1 clause)
+ (propagate0)))
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (propagate2 kbody kalt)
+ (propagate1 kbody)))
+ (($ $ktail) (propagate0)))))
+
+ (let ((boolv (worklist-fold* visit-cont
+ (intset kfun)
+ (intmap-add boolv kfun empty-intset))))
+ ;; Now visit nested functions. We don't do this in the worklist
+ ;; folder because that would be exponential.
+ (define (recurse kfun boolv)
+ (compute-truthy-expressions conts kfun boolv))
+ (intset-fold
+ (lambda (label boolv)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $fun kfun) (recurse kfun boolv))
+ (($ $rec _ _ (($ $fun kfun) ...)) (fold recurse boolv kfun))
+ (_ boolv)))
+ (_ boolv)))
+ (compute-function-body conts kfun)
+ boolv)))
+
+(define (intset-map f set)
+ (persistent-intmap
+ (intset-fold (lambda (i out) (intmap-add! out i (f i)))
+ set
+ empty-intmap)))
+
+;; Returns a map of label-idx -> (var-idx ...) indicating the variables
+;; defined by a given labelled expression.
+(define (compute-defs conts kfun)
+ (intset-map (lambda (label)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail clause)
+ (list self))
+ (($ $kclause arity body alt)
+ (match (intmap-ref conts body)
+ (($ $kargs names vars) vars)))
+ (($ $kreceive arity kargs)
+ (match (intmap-ref conts kargs)
+ (($ $kargs names vars) vars)))
+ (($ $ktail)
+ '())
+ (($ $kargs names vars ($ $continue k))
+ (match (intmap-ref conts k)
+ (($ $kargs names vars) vars)
+ (_ #f)))))
+ (compute-function-body conts kfun)))
+
+(define (compute-singly-referenced succs)
+ (define (visit label succs single multiple)
+ (intset-fold (lambda (label single multiple)
+ (if (intset-ref single label)
+ (values single (intset-add! multiple label))
+ (values (intset-add! single label) multiple)))
+ succs single multiple))
+ (call-with-values (lambda ()
+ (intmap-fold visit succs empty-intset empty-intset))
+ (lambda (single multiple)
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple)))))
+
+(define (compute-equivalent-subexpressions conts kfun effects
+ equiv-labels var-substs)
+ (let* ((succs (compute-successors conts kfun))
+ (singly-referenced (compute-singly-referenced succs))
+ (avail (compute-available-expressions conts kfun effects))
+ (defs (compute-defs conts kfun))
+ (equiv-set (make-hash-table)))
+ (define (subst-var var-substs var)
+ (intmap-ref var-substs var (lambda (var) var)))
+ (define (subst-vars var-substs vars)
+ (let lp ((vars vars))
+ (match vars
+ (() '())
+ ((var . vars) (cons (subst-var var-substs var) (lp vars))))))
+
+ (define (compute-exp-key var-substs exp)
+ (match exp
+ (($ $const val) (cons 'const val))
+ (($ $prim name) (cons 'prim name))
+ (($ $fun body) #f)
+ (($ $rec names syms funs) #f)
+ (($ $call proc args) #f)
+ (($ $callk k proc args) #f)
+ (($ $primcall name args)
+ (cons* 'primcall name (subst-vars var-substs args)))
+ (($ $branch _ ($ $primcall name args))
+ (cons* 'primcall name (subst-vars var-substs args)))
+ (($ $branch) #f)
+ (($ $values args) #f)
+ (($ $prompt escape? tag handler) #f)))
+
+ (define (add-auxiliary-definitions! label var-substs exp-key)
+ (define (subst var)
+ (subst-var var-substs var))
+ (let ((defs (intmap-ref defs label)))
+ (define (add-def! aux-key var)
+ (let ((equiv (hash-ref equiv-set aux-key '())))
+ (hash-set! equiv-set aux-key
+ (acons label (list var) equiv))))
+ (match exp-key
+ (('primcall 'box val)
+ (match defs
+ ((box)
+ (add-def! `(primcall box-ref ,(subst box)) val))))
+ (('primcall 'box-set! box val)
+ (add-def! `(primcall box-ref ,box) val))
+ (('primcall 'cons car cdr)
+ (match defs
+ ((pair)
+ (add-def! `(primcall car ,(subst pair)) car)
+ (add-def! `(primcall cdr ,(subst pair)) cdr))))
+ (('primcall 'set-car! pair car)
+ (add-def! `(primcall car ,pair) car))
+ (('primcall 'set-cdr! pair cdr)
+ (add-def! `(primcall cdr ,pair) cdr))
+ (('primcall (or 'make-vector 'make-vector/immediate) len fill)
+ (match defs
+ ((vec)
+ (add-def! `(primcall vector-length ,(subst vec)) len))))
+ (('primcall 'vector-set! vec idx val)
+ (add-def! `(primcall vector-ref ,vec ,idx) val))
+ (('primcall 'vector-set!/immediate vec idx val)
+ (add-def! `(primcall vector-ref/immediate ,vec ,idx) val))
+ (('primcall (or 'allocate-struct 'allocate-struct/immediate)
+ vtable size)
+ (match defs
+ ((struct)
+ (add-def! `(primcall struct-vtable ,(subst struct))
+ vtable))))
+ (('primcall 'struct-set! struct n val)
+ (add-def! `(primcall struct-ref ,struct ,n) val))
+ (('primcall 'struct-set!/immediate struct n val)
+ (add-def! `(primcall struct-ref/immediate ,struct ,n) val))
+ (_ #t))))
+
+ (define (visit-label label equiv-labels var-substs)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (let* ((exp-key (compute-exp-key var-substs exp))
+ (equiv (hash-ref equiv-set exp-key '()))
+ (fx (intmap-ref effects label))
+ (avail (intmap-ref avail label)))
+ (define (finish equiv-labels var-substs)
+ (define (recurse kfun equiv-labels var-substs)
+ (compute-equivalent-subexpressions conts kfun effects
+ equiv-labels var-substs))
+ ;; If this expression defines auxiliary definitions,
+ ;; as `cons' does for the results of `car' and `cdr',
+ ;; define those. Do so after finding equivalent
+ ;; expressions, so that we can take advantage of
+ ;; subst'd output vars.
+ (add-auxiliary-definitions! label var-substs exp-key)
+ (match exp
+ ;; If we see a $fun, recurse to add to the result.
+ (($ $fun kfun)
+ (recurse kfun equiv-labels var-substs))
+ (($ $rec names vars (($ $fun kfun) ...))
+ (fold2 recurse kfun equiv-labels var-substs))
+ (_
+ (values equiv-labels var-substs))))
+ (let lp ((candidates equiv))
+ (match candidates
+ (()
+ ;; No matching expressions. Add our expression
+ ;; to the equivalence set, if appropriate. Note
+ ;; that expressions that allocate a fresh object
+ ;; or change the current fluid environment can't
+ ;; be eliminated by CSE (though DCE might do it
+ ;; if the value proves to be unused, in the
+ ;; allocation case).
+ (when (and exp-key
+ (not (causes-effect? fx &allocation))
+ (not (effect-clobbers? fx (&read-object &fluid))))
+ (let ((defs (and (intset-ref singly-referenced k)
+ (intmap-ref defs label))))
+ (when defs
+ (hash-set! equiv-set exp-key
+ (acons label defs equiv)))))
+ (finish equiv-labels var-substs))
+ (((and head (candidate . vars)) . candidates)
+ (cond
+ ((not (intset-ref avail candidate))
+ ;; This expression isn't available here; try
+ ;; the next one.
+ (lp candidates))
+ (else
+ ;; Yay, a match. Mark expression as equivalent. If
+ ;; we provide the definitions for the successor, mark
+ ;; the vars for substitution.
+ (finish (intmap-add equiv-labels label head)
+ (let ((defs (and (intset-ref singly-referenced k)
+ (intmap-ref defs label))))
+ (if defs
+ (fold (lambda (def var var-substs)
+ (intmap-add var-substs def var))
+ var-substs defs vars)
+ var-substs))))))))))
+ (_ (values equiv-labels var-substs))))
+
+ ;; Traverse the labels in fun in reverse post-order, which will
+ ;; visit definitions before uses first.
+ (fold2 visit-label
+ (compute-reverse-post-order succs kfun)
+ equiv-labels
+ var-substs)))
+
+(define (apply-cse conts equiv-labels var-substs truthy-labels)
+ (define (true-idx idx) (ash idx 1))
+ (define (false-idx idx) (1+ (ash idx 1)))
+
+ (define (subst-var var)
+ (intmap-ref var-substs var (lambda (var) var)))
+
+ (define (visit-exp exp)
+ (rewrite-exp exp
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) ,exp)
+ (($ $call proc args)
+ ($call (subst-var proc) ,(map subst-var args)))
+ (($ $callk k proc args)
+ ($callk k (subst-var proc) ,(map subst-var args)))
+ (($ $primcall name args)
+ ($primcall name ,(map subst-var args)))
+ (($ $branch k exp)
+ ($branch k ,(visit-exp exp)))
+ (($ $values args)
+ ($values ,(map subst-var args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (subst-var tag) handler))))
+
+ (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (build-cont
+ ($kargs names vars
+ ,(match (intmap-ref equiv-labels label (lambda (_) #f))
+ ((equiv . vars)
+ (match exp
+ (($ $branch kt exp)
+ (let* ((bool (intmap-ref truthy-labels label))
+ (t (intset-ref bool (true-idx equiv)))
+ (f (intset-ref bool (false-idx equiv))))
+ (if (eqv? t f)
+ (build-term
+ ($continue k src
+ ($branch kt ,(visit-exp exp))))
+ (build-term
+ ($continue (if t kt k) src ($values ()))))))
+ (_
+ ;; For better or for worse, we only replace primcalls
+ ;; if they have an associated VM op, which allows
+ ;; them to continue to $kargs and thus we know their
+ ;; defs and can use a $values expression instead of a
+ ;; values primcall.
+ (build-term
+ ($continue k src ($values vars))))))
+ (#f
+ (build-term
+ ($continue k src ,(visit-exp exp))))))))
+ (_ cont)))
+ conts))
+
+(define (eliminate-common-subexpressions conts)
+ (call-with-values
+ (lambda ()
+ (let ((effects (synthesize-definition-effects (compute-effects conts))))
+ (compute-equivalent-subexpressions conts 0 effects
+ empty-intmap empty-intmap)))
+ (lambda (equiv-labels var-substs)
+ (let ((truthy-labels (compute-truthy-expressions conts 0 empty-intmap)))
+ (apply-cse conts equiv-labels var-substs truthy-labels)))))
diff --git a/module/language/cps/dce.scm b/module/language/cps/dce.scm
new file mode 100644
index 000000000..5463f5b1e
--- /dev/null
+++ b/module/language/cps/dce.scm
@@ -0,0 +1,399 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; This pass kills dead expressions: code that has no side effects, and
+;;; whose value is unused. It does so by marking all live values, and
+;;; then discarding other values as dead. This happens recursively
+;;; through procedures, so it should be possible to elide dead
+;;; procedures as well.
+;;;
+;;; Code:
+
+(define-module (language cps dce)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (language cps)
+ #:use-module (language cps effects-analysis)
+ #:use-module (language cps renumber)
+ #:use-module (language cps types)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (eliminate-dead-code))
+
+(define (elide-type-checks conts kfun effects)
+ "Elide &type-check effects from EFFECTS for the function starting at
+KFUN where we can prove that no assertion will be raised at run-time."
+ (let ((types (infer-types conts kfun)))
+ (define (visit-primcall effects fx label name args)
+ (if (primcall-types-check? types label name args)
+ (intmap-replace! effects label (logand fx (lognot &type-check)))
+ effects))
+ (persistent-intmap
+ (intmap-fold (lambda (label types effects)
+ (let ((fx (intmap-ref effects label)))
+ (cond
+ ((causes-all-effects? fx) effects)
+ ((causes-effect? fx &type-check)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ exp)
+ (match exp
+ (($ $continue k src ($ $primcall name args))
+ (visit-primcall effects fx label name args))
+ (($ $continue k src
+ ($ $branch _ ($primcall name args)))
+ (visit-primcall effects fx label name args))
+ (_ effects)))
+ (_ effects)))
+ (else effects))))
+ types
+ effects))))
+
+(define (compute-effects/elide-type-checks conts)
+ (intmap-fold (lambda (label cont effects)
+ (match cont
+ (($ $kfun) (elide-type-checks conts label effects))
+ (_ effects)))
+ conts
+ (compute-effects conts)))
+
+(define (fold-local-conts proc conts label seed)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail clause)
+ (let lp ((label label) (seed seed))
+ (if (<= label tail)
+ (lp (1+ label) (proc label (intmap-ref conts label) seed))
+ seed)))))
+
+(define (postorder-fold-local-conts2 proc conts label seed0 seed1)
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail clause)
+ (let ((start label))
+ (let lp ((label tail) (seed0 seed0) (seed1 seed1))
+ (if (<= start label)
+ (let ((cont (intmap-ref conts label)))
+ (call-with-values (lambda () (proc label cont seed0 seed1))
+ (lambda (seed0 seed1)
+ (lp (1- label) seed0 seed1))))
+ (values seed0 seed1)))))))
+
+(define (compute-known-allocations conts effects)
+ "Compute the variables bound in CONTS that have known allocation
+sites."
+ ;; Compute the set of conts that are called with freshly allocated
+ ;; values, and subtract from that set the conts that might be called
+ ;; with values with unknown allocation sites. Then convert that set
+ ;; of conts into a set of bound variables.
+ (call-with-values
+ (lambda ()
+ (intmap-fold (lambda (label cont known unknown)
+ ;; Note that we only need to add labels to the
+ ;; known/unknown sets if the labels can bind
+ ;; values. So there's no need to add tail,
+ ;; clause, branch alternate, or prompt handler
+ ;; labels, as they bind no values.
+ (match cont
+ (($ $kargs _ _ ($ $continue k))
+ (let ((fx (intmap-ref effects label)))
+ (if (and (not (causes-all-effects? fx))
+ (causes-effect? fx &allocation))
+ (values (intset-add! known k) unknown)
+ (values known (intset-add! unknown k)))))
+ (($ $kreceive arity kargs)
+ (values known (intset-add! unknown kargs)))
+ (($ $kfun src meta self tail clause)
+ (values known unknown))
+ (($ $kclause arity body alt)
+ (values known (intset-add! unknown body)))
+ (($ $ktail)
+ (values known unknown))))
+ conts
+ empty-intset
+ empty-intset))
+ (lambda (known unknown)
+ (persistent-intset
+ (intset-fold (lambda (label vars)
+ (match (intmap-ref conts label)
+ (($ $kargs (_) (var)) (intset-add! vars var))
+ (_ vars)))
+ (intset-subtract (persistent-intset known)
+ (persistent-intset unknown))
+ empty-intset)))))
+
+(define (compute-live-code conts)
+ (let* ((effects (compute-effects/elide-type-checks conts))
+ (known-allocations (compute-known-allocations conts effects)))
+ (define (adjoin-var var set)
+ (intset-add set var))
+ (define (adjoin-vars vars set)
+ (match vars
+ (() set)
+ ((var . vars) (adjoin-vars vars (adjoin-var var set)))))
+ (define (var-live? var live-vars)
+ (intset-ref live-vars var))
+ (define (any-var-live? vars live-vars)
+ (match vars
+ (() #f)
+ ((var . vars)
+ (or (var-live? var live-vars)
+ (any-var-live? vars live-vars)))))
+ (define (cont-defs k)
+ (match (intmap-ref conts k)
+ (($ $kargs _ vars) vars)
+ (_ #f)))
+
+ (define (visit-live-exp label k exp live-labels live-vars)
+ (match exp
+ ((or ($ $const) ($ $prim))
+ (values live-labels live-vars))
+ (($ $fun body)
+ (values (intset-add live-labels body) live-vars))
+ (($ $closure body)
+ (values (intset-add live-labels body) live-vars))
+ (($ $rec names vars (($ $fun kfuns) ...))
+ (let lp ((vars vars) (kfuns kfuns)
+ (live-labels live-labels) (live-vars live-vars))
+ (match (vector vars kfuns)
+ (#(() ()) (values live-labels live-vars))
+ (#((var . vars) (kfun . kfuns))
+ (lp vars kfuns
+ (if (var-live? var live-vars)
+ (intset-add live-labels kfun)
+ live-labels)
+ live-vars)))))
+ (($ $prompt escape? tag handler)
+ (values live-labels (adjoin-var tag live-vars)))
+ (($ $call proc args)
+ (values live-labels (adjoin-vars args (adjoin-var proc live-vars))))
+ (($ $callk kfun proc args)
+ (values (intset-add live-labels kfun)
+ (adjoin-vars args (adjoin-var proc live-vars))))
+ (($ $primcall name args)
+ (values live-labels (adjoin-vars args live-vars)))
+ (($ $branch k ($ $primcall name args))
+ (values live-labels (adjoin-vars args live-vars)))
+ (($ $branch k ($ $values (arg)))
+ (values live-labels (adjoin-var arg live-vars)))
+ (($ $values args)
+ (values live-labels
+ (match (cont-defs k)
+ (#f (adjoin-vars args live-vars))
+ (defs (fold (lambda (use def live-vars)
+ (if (var-live? def live-vars)
+ (adjoin-var use live-vars)
+ live-vars))
+ live-vars args defs)))))))
+
+ (define (visit-exp label k exp live-labels live-vars)
+ (cond
+ ((intset-ref live-labels label)
+ ;; Expression live already.
+ (visit-live-exp label k exp live-labels live-vars))
+ ((let ((defs (cont-defs k))
+ (fx (intmap-ref effects label)))
+ (or
+ ;; No defs; perhaps continuation is $ktail.
+ (not defs)
+ ;; We don't remove branches.
+ (match exp (($ $branch) #t) (_ #f))
+ ;; Do we have a live def?
+ (any-var-live? defs live-vars)
+ ;; Does this expression cause all effects? If so, it's
+ ;; definitely live.
+ (causes-all-effects? fx)
+ ;; Does it cause a type check, but we weren't able to prove
+ ;; that the types check?
+ (causes-effect? fx &type-check)
+ ;; We might have a setter. If the object being assigned to
+ ;; is live or was not created by us, then this expression is
+ ;; live. Otherwise the value is still dead.
+ (and (causes-effect? fx &write)
+ (match exp
+ (($ $primcall
+ (or 'vector-set! 'vector-set!/immediate
+ 'set-car! 'set-cdr!
+ 'box-set!)
+ (obj . _))
+ (or (var-live? obj live-vars)
+ (not (intset-ref known-allocations obj))))
+ (_ #t)))))
+ ;; Mark expression as live and visit.
+ (visit-live-exp label k exp (intset-add live-labels label) live-vars))
+ (else
+ ;; Still dead.
+ (values live-labels live-vars))))
+
+ (define (visit-fun label live-labels live-vars)
+ ;; Visit uses before definitions.
+ (postorder-fold-local-conts2
+ (lambda (label cont live-labels live-vars)
+ (match cont
+ (($ $kargs _ _ ($ $continue k src exp))
+ (visit-exp label k exp live-labels live-vars))
+ (($ $kreceive arity kargs)
+ (values live-labels live-vars))
+ (($ $kclause arity kargs kalt)
+ (values live-labels (adjoin-vars (cont-defs kargs) live-vars)))
+ (($ $kfun src meta self)
+ (values live-labels (adjoin-var self live-vars)))
+ (($ $ktail)
+ (values live-labels live-vars))))
+ conts label live-labels live-vars))
+
+ (fixpoint (lambda (live-labels live-vars)
+ (let lp ((label 0)
+ (live-labels live-labels)
+ (live-vars live-vars))
+ (match (intset-next live-labels label)
+ (#f (values live-labels live-vars))
+ (label
+ (call-with-values
+ (lambda ()
+ (match (intmap-ref conts label)
+ (($ $kfun)
+ (visit-fun label live-labels live-vars))
+ (_ (values live-labels live-vars))))
+ (lambda (live-labels live-vars)
+ (lp (1+ label) live-labels live-vars)))))))
+ (intset 0)
+ empty-intset)))
+
+(define-syntax adjoin-conts
+ (syntax-rules ()
+ ((_ (exp ...) clause ...)
+ (let ((cps (exp ...)))
+ (adjoin-conts cps clause ...)))
+ ((_ cps (label cont) clause ...)
+ (adjoin-conts (intmap-add! cps label (build-cont cont))
+ clause ...))
+ ((_ cps)
+ cps)))
+
+(define (process-eliminations conts live-labels live-vars)
+ (define (label-live? label)
+ (intset-ref live-labels label))
+ (define (value-live? var)
+ (intset-ref live-vars var))
+ (define (make-adaptor k src defs)
+ (let* ((names (map (lambda (_) 'tmp) defs))
+ (vars (map (lambda (_) (fresh-var)) defs))
+ (live (filter-map (lambda (def var)
+ (and (value-live? def) var))
+ defs vars)))
+ (build-cont
+ ($kargs names vars
+ ($continue k src ($values live))))))
+ (define (visit-term label term cps)
+ (match term
+ (($ $continue k src exp)
+ (if (label-live? label)
+ (match exp
+ (($ $fun body)
+ (values cps
+ term))
+ (($ $closure body nfree)
+ (values cps
+ term))
+ (($ $rec names vars funs)
+ (match (filter-map (lambda (name var fun)
+ (and (value-live? var)
+ (list name var fun)))
+ names vars funs)
+ (()
+ (values cps
+ (build-term ($continue k src ($values ())))))
+ (((names vars funs) ...)
+ (values cps
+ (build-term ($continue k src
+ ($rec names vars funs)))))))
+ (_
+ (match (intmap-ref conts k)
+ (($ $kargs ())
+ (values cps term))
+ (($ $kargs names ((? value-live?) ...))
+ (values cps term))
+ (($ $kargs names vars)
+ (match exp
+ (($ $values args)
+ (let ((args (filter-map (lambda (use def)
+ (and (value-live? def) use))
+ args vars)))
+ (values cps
+ (build-term
+ ($continue k src ($values args))))))
+ (_
+ (let-fresh (adapt) ()
+ (values (adjoin-conts cps
+ (adapt ,(make-adaptor k src vars)))
+ (build-term
+ ($continue adapt src ,exp)))))))
+ (_
+ (values cps term)))))
+ (values cps
+ (build-term
+ ($continue k src ($values ()))))))))
+ (define (visit-cont label cont cps)
+ (match cont
+ (($ $kargs names vars term)
+ (match (filter-map (lambda (name var)
+ (and (value-live? var)
+ (cons name var)))
+ names vars)
+ (((names . vars) ...)
+ (call-with-values (lambda () (visit-term label term cps))
+ (lambda (cps term)
+ (adjoin-conts cps
+ (label ($kargs names vars ,term))))))))
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (let ((defs (match (intmap-ref conts kargs)
+ (($ $kargs names vars) vars))))
+ (if (and-map value-live? defs)
+ (adjoin-conts cps (label ,cont))
+ (let-fresh (adapt) ()
+ (adjoin-conts cps
+ (adapt ,(make-adaptor kargs #f defs))
+ (label ($kreceive req rest adapt)))))))
+ (_
+ (adjoin-conts cps (label ,cont)))))
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (intmap-fold (lambda (label cont cps)
+ (match cont
+ (($ $kfun)
+ (if (label-live? label)
+ (fold-local-conts visit-cont conts label cps)
+ cps))
+ (_ cps)))
+ conts
+ empty-intmap))))
+
+(define (eliminate-dead-code conts)
+ ;; We work on a renumbered program so that we can easily visit uses
+ ;; before definitions just by visiting higher-numbered labels before
+ ;; lower-numbered labels. Renumbering is also a precondition for type
+ ;; inference.
+ (let ((conts (renumber conts)))
+ (call-with-values (lambda () (compute-live-code conts))
+ (lambda (live-labels live-vars)
+ (process-eliminations conts live-labels live-vars)))))
+
+;;; Local Variables:
+;;; eval: (put 'adjoin-conts 'scheme-indent-function 1)
+;;; End:
diff --git a/module/language/cps/effects-analysis.scm b/module/language/cps/effects-analysis.scm
new file mode 100644
index 000000000..874eb7829
--- /dev/null
+++ b/module/language/cps/effects-analysis.scm
@@ -0,0 +1,484 @@
+;;; Effects analysis on CPS
+
+;; Copyright (C) 2011, 2012, 2013, 2014, 2015 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:
+;;;
+;;; A helper module to compute the set of effects caused by an
+;;; expression. This information is useful when writing algorithms that
+;;; move code around, while preserving the semantics of an input
+;;; program.
+;;;
+;;; The effects set is represented as an integer with three parts. The
+;;; low 4 bits indicate effects caused by an expression, as a bitfield.
+;;; The next 4 bits indicate the kind of memory accessed by the
+;;; expression, if it accesses mutable memory. Finally the rest of the
+;;; bits indicate the field in the object being accessed, if known, or
+;;; -1 for unknown.
+;;;
+;;; In this way we embed a coarse type-based alias analysis in the
+;;; effects analysis. For example, a "car" call is modelled as causing
+;;; a read to field 0 on a &pair, and causing a &type-check effect. If
+;;; any intervening code sets the car of any pair, that will block
+;;; motion of the "car" call, because any write to field 0 of a pair is
+;;; seen by effects analysis as being a write to field 0 of all pairs.
+;;;
+;;; Code:
+
+(define-module (language cps effects-analysis)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (ice-9 match)
+ #:export (expression-effects
+ compute-effects
+ synthesize-definition-effects
+
+ &allocation
+ &type-check
+ &read
+ &write
+
+ &fluid
+ &prompt
+ &car
+ &cdr
+ &vector
+ &box
+ &module
+ &struct
+ &string
+ &bytevector
+
+ &object
+ &field
+
+ &allocate
+ &read-object
+ &read-field
+ &write-object
+ &write-field
+
+ &no-effects
+ &all-effects
+
+ exclude-effects
+ effect-free?
+ constant?
+ causes-effect?
+ causes-all-effects?
+ effect-clobbers?))
+
+(define-syntax define-flags
+ (lambda (x)
+ (syntax-case x ()
+ ((_ all shift name ...)
+ (let ((count (length #'(name ...))))
+ (with-syntax (((n ...) (iota count))
+ (count count))
+ #'(begin
+ (define-syntax name (identifier-syntax (ash 1 n)))
+ ...
+ (define-syntax all (identifier-syntax (1- (ash 1 count))))
+ (define-syntax shift (identifier-syntax count)))))))))
+
+(define-syntax define-enumeration
+ (lambda (x)
+ (define (count-bits n)
+ (let lp ((out 1))
+ (if (< n (ash 1 (1- out)))
+ out
+ (lp (1+ out)))))
+ (syntax-case x ()
+ ((_ mask shift name ...)
+ (let* ((len (length #'(name ...)))
+ (bits (count-bits len)))
+ (with-syntax (((n ...) (iota len))
+ (bits bits))
+ #'(begin
+ (define-syntax name (identifier-syntax n))
+ ...
+ (define-syntax mask (identifier-syntax (1- (ash 1 bits))))
+ (define-syntax shift (identifier-syntax bits)))))))))
+
+(define-flags &all-effect-kinds &effect-kind-bits
+ ;; Indicates that an expression may cause a type check. A type check,
+ ;; for the purposes of this analysis, is the possibility of throwing
+ ;; an exception the first time an expression is evaluated. If the
+ ;; expression did not cause an exception to be thrown, users can
+ ;; assume that evaluating the expression again will not cause an
+ ;; exception to be thrown.
+ ;;
+ ;; For example, (+ x y) might throw if X or Y are not numbers. But if
+ ;; it doesn't throw, it should be safe to elide a dominated, common
+ ;; subexpression (+ x y).
+ &type-check
+
+ ;; Indicates that an expression may return a fresh object. The kind
+ ;; of object is indicated in the object kind field.
+ &allocation
+
+ ;; Indicates that an expression may cause a read from memory. The
+ ;; kind of memory is given in the object kind field. Some object
+ ;; kinds have finer-grained fields; those are expressed in the "field"
+ ;; part of the effects value. -1 indicates "the whole object".
+ &read
+
+ ;; Indicates that an expression may cause a write to memory.
+ &write)
+
+(define-enumeration &memory-kind-mask &memory-kind-bits
+ ;; Indicates than an expression may access unknown kinds of memory.
+ &unknown-memory-kinds
+
+ ;; Indicates that an expression depends on the value of a fluid
+ ;; variable, or on the current fluid environment.
+ &fluid
+
+ ;; Indicates that an expression depends on the current prompt
+ ;; stack.
+ &prompt
+
+ ;; Indicates that an expression depends on the value of the car or cdr
+ ;; of a pair.
+ &pair
+
+ ;; Indicates that an expression depends on the value of a vector
+ ;; field. The effect field indicates the specific field, or zero for
+ ;; an unknown field.
+ &vector
+
+ ;; Indicates that an expression depends on the value of a variable
+ ;; cell.
+ &box
+
+ ;; Indicates that an expression depends on the current module.
+ &module
+
+ ;; Indicates that an expression depends on the value of a struct
+ ;; field. The effect field indicates the specific field, or zero for
+ ;; an unknown field.
+ &struct
+
+ ;; Indicates that an expression depends on the contents of a string.
+ &string
+
+ ;; Indicates that an expression depends on the contents of a
+ ;; bytevector. We cannot be more precise, as bytevectors may alias
+ ;; other bytevectors.
+ &bytevector)
+
+(define-inlinable (&field kind field)
+ (ash (logior (ash field &memory-kind-bits) kind) &effect-kind-bits))
+(define-inlinable (&object kind)
+ (&field kind -1))
+
+(define-inlinable (&allocate kind)
+ (logior &allocation (&object kind)))
+(define-inlinable (&read-field kind field)
+ (logior &read (&field kind field)))
+(define-inlinable (&read-object kind)
+ (logior &read (&object kind)))
+(define-inlinable (&write-field kind field)
+ (logior &write (&field kind field)))
+(define-inlinable (&write-object kind)
+ (logior &write (&object kind)))
+
+(define-syntax &no-effects (identifier-syntax 0))
+(define-syntax &all-effects
+ (identifier-syntax
+ (logior &all-effect-kinds (&object &unknown-memory-kinds))))
+
+(define-inlinable (constant? effects)
+ (zero? effects))
+
+(define-inlinable (causes-effect? x effects)
+ (not (zero? (logand x effects))))
+
+(define-inlinable (causes-all-effects? x)
+ (eqv? x &all-effects))
+
+(define (effect-clobbers? a b)
+ "Return true if A clobbers B. This is the case if A is a write, and B
+is or might be a read or a write to the same location as A."
+ (define (locations-same?)
+ (let ((a (ash a (- &effect-kind-bits)))
+ (b (ash b (- &effect-kind-bits))))
+ (or (eqv? &unknown-memory-kinds (logand a &memory-kind-mask))
+ (eqv? &unknown-memory-kinds (logand b &memory-kind-mask))
+ (and (eqv? (logand a &memory-kind-mask) (logand b &memory-kind-mask))
+ ;; A negative field indicates "the whole object".
+ ;; Non-negative fields indicate only part of the object.
+ (or (< a 0) (< b 0) (= a b))))))
+ (and (not (zero? (logand a &write)))
+ (not (zero? (logand b (logior &read &write))))
+ (locations-same?)))
+
+(define-inlinable (indexed-field kind var constants)
+ (let ((val (intmap-ref constants var (lambda (_) #f))))
+ (if (and (exact-integer? val) (<= 0 val))
+ (&field kind val)
+ (&object kind))))
+
+(define *primitive-effects* (make-hash-table))
+
+(define-syntax-rule (define-primitive-effects* constants
+ ((name . args) effects ...)
+ ...)
+ (begin
+ (hashq-set! *primitive-effects* 'name
+ (case-lambda*
+ ((constants . args) (logior effects ...))
+ (_ &all-effects)))
+ ...))
+
+(define-syntax-rule (define-primitive-effects ((name . args) effects ...) ...)
+ (define-primitive-effects* constants ((name . args) effects ...) ...))
+
+;; Miscellaneous.
+(define-primitive-effects
+ ((values . _)))
+
+;; Generic effect-free predicates.
+(define-primitive-effects
+ ((eq? . _))
+ ((eqv? . _))
+ ((equal? . _))
+ ((pair? arg))
+ ((null? arg))
+ ((nil? arg ))
+ ((symbol? arg))
+ ((variable? arg))
+ ((vector? arg))
+ ((struct? arg))
+ ((string? arg))
+ ((number? arg))
+ ((char? arg))
+ ((bytevector? arg))
+ ((keyword? arg))
+ ((bitvector? arg))
+ ((procedure? arg))
+ ((thunk? arg)))
+
+;; Fluids.
+(define-primitive-effects
+ ((fluid-ref f) (&read-object &fluid) &type-check)
+ ((fluid-set! f v) (&write-object &fluid) &type-check)
+ ((push-fluid f v) (&write-object &fluid) &type-check)
+ ((pop-fluid) (&write-object &fluid) &type-check))
+
+;; Prompts.
+(define-primitive-effects
+ ((make-prompt-tag #:optional arg) (&allocate &unknown-memory-kinds)))
+
+;; Pairs.
+(define-primitive-effects
+ ((cons a b) (&allocate &pair))
+ ((list . _) (&allocate &pair))
+ ((car x) (&read-field &pair 0) &type-check)
+ ((set-car! x y) (&write-field &pair 0) &type-check)
+ ((cdr x) (&read-field &pair 1) &type-check)
+ ((set-cdr! x y) (&write-field &pair 1) &type-check)
+ ((memq x y) (&read-object &pair) &type-check)
+ ((memv x y) (&read-object &pair) &type-check)
+ ((list? arg) (&read-field &pair 1))
+ ((length l) (&read-field &pair 1) &type-check))
+
+;; Variables.
+(define-primitive-effects
+ ((box v) (&allocate &box))
+ ((box-ref v) (&read-object &box) &type-check)
+ ((box-set! v x) (&write-object &box) &type-check))
+
+;; Vectors.
+(define (vector-field n constants)
+ (indexed-field &vector n constants))
+(define (read-vector-field n constants)
+ (logior &read (vector-field n constants)))
+(define (write-vector-field n constants)
+ (logior &write (vector-field n constants)))
+(define-primitive-effects* constants
+ ((vector . _) (&allocate &vector))
+ ((make-vector n init) (&allocate &vector) &type-check)
+ ((make-vector/immediate n init) (&allocate &vector))
+ ((vector-ref v n) (read-vector-field n constants) &type-check)
+ ((vector-ref/immediate v n) (read-vector-field n constants) &type-check)
+ ((vector-set! v n x) (write-vector-field n constants) &type-check)
+ ((vector-set!/immediate v n x) (write-vector-field n constants) &type-check)
+ ((vector-length v) &type-check))
+
+;; Structs.
+(define (struct-field n constants)
+ (indexed-field &struct n constants))
+(define (read-struct-field n constants)
+ (logior &read (struct-field n constants)))
+(define (write-struct-field n constants)
+ (logior &write (struct-field n constants)))
+(define-primitive-effects* constants
+ ((allocate-struct vt n) (&allocate &struct) &type-check)
+ ((allocate-struct/immediate v n) (&allocate &struct) &type-check)
+ ((make-struct vt ntail . _) (&allocate &struct) &type-check)
+ ((make-struct/no-tail vt . _) (&allocate &struct) &type-check)
+ ((struct-ref s n) (read-struct-field n constants) &type-check)
+ ((struct-ref/immediate s n) (read-struct-field n constants) &type-check)
+ ((struct-set! s n x) (write-struct-field n constants) &type-check)
+ ((struct-set!/immediate s n x) (write-struct-field n constants) &type-check)
+ ((struct-vtable s) &type-check))
+
+;; Strings.
+(define-primitive-effects
+ ((string-ref s n) (&read-object &string) &type-check)
+ ((string-set! s n c) (&write-object &string) &type-check)
+ ((number->string _) (&allocate &string) &type-check)
+ ((string->number _) (&read-object &string) &type-check)
+ ((string-length s) &type-check))
+
+;; Bytevectors.
+(define-primitive-effects
+ ((bytevector-length _) &type-check)
+
+ ((bv-u8-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-s8-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-u16-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-s16-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-u32-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-s32-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-u64-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-s64-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-f32-ref bv n) (&read-object &bytevector) &type-check)
+ ((bv-f64-ref bv n) (&read-object &bytevector) &type-check)
+
+ ((bv-u8-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-s8-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-u16-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-s16-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-u32-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-s32-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-u64-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-s64-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-f32-set! bv n x) (&write-object &bytevector) &type-check)
+ ((bv-f64-set! bv n x) (&write-object &bytevector) &type-check))
+
+;; Modules.
+(define-primitive-effects
+ ((current-module) (&read-object &module))
+ ((cache-current-module! m scope) (&write-object &box))
+ ((resolve name bound?) (&read-object &module) &type-check)
+ ((cached-toplevel-box scope name bound?) &type-check)
+ ((cached-module-box mod name public? bound?) &type-check)
+ ((define! name val) (&read-object &module) (&write-object &box)))
+
+;; Numbers.
+(define-primitive-effects
+ ((= . _) &type-check)
+ ((< . _) &type-check)
+ ((> . _) &type-check)
+ ((<= . _) &type-check)
+ ((>= . _) &type-check)
+ ((zero? . _) &type-check)
+ ((add . _) &type-check)
+ ((mul . _) &type-check)
+ ((sub . _) &type-check)
+ ((div . _) &type-check)
+ ((sub1 . _) &type-check)
+ ((add1 . _) &type-check)
+ ((quo . _) &type-check)
+ ((rem . _) &type-check)
+ ((mod . _) &type-check)
+ ((complex? _) &type-check)
+ ((real? _) &type-check)
+ ((rational? _) &type-check)
+ ((inf? _) &type-check)
+ ((nan? _) &type-check)
+ ((integer? _) &type-check)
+ ((exact? _) &type-check)
+ ((inexact? _) &type-check)
+ ((even? _) &type-check)
+ ((odd? _) &type-check)
+ ((ash n m) &type-check)
+ ((logand . _) &type-check)
+ ((logior . _) &type-check)
+ ((logxor . _) &type-check)
+ ((lognot . _) &type-check)
+ ((logtest a b) &type-check)
+ ((logbit? a b) &type-check)
+ ((sqrt _) &type-check)
+ ((abs _) &type-check))
+
+;; Characters.
+(define-primitive-effects
+ ((char . _) &type-check)
+ ((char<=? . _) &type-check)
+ ((char>=? . _) &type-check)
+ ((char>? . _) &type-check)
+ ((integer->char _) &type-check)
+ ((char->integer _) &type-check))
+
+(define (primitive-effects constants name args)
+ (let ((proc (hashq-ref *primitive-effects* name)))
+ (if proc
+ (apply proc constants args)
+ &all-effects)))
+
+(define (expression-effects exp constants)
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $values))
+ &no-effects)
+ ((or ($ $fun) ($ $rec) ($ $closure))
+ (&allocate &unknown-memory-kinds))
+ (($ $prompt)
+ (&write-object &prompt))
+ ((or ($ $call) ($ $callk))
+ &all-effects)
+ (($ $branch k exp)
+ (expression-effects exp constants))
+ (($ $primcall name args)
+ (primitive-effects constants name args))))
+
+(define (compute-effects conts)
+ (let ((constants (compute-constant-values conts)))
+ (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names syms ($ $continue k src exp))
+ (expression-effects exp constants))
+ (($ $kreceive arity kargs)
+ (match arity
+ (($ $arity _ () #f () #f) &type-check)
+ (($ $arity () () _ () #f) (&allocate &pair))
+ (($ $arity _ () _ () #f) (logior (&allocate &pair) &type-check))))
+ (($ $kfun) &type-check)
+ (($ $kclause) &type-check)
+ (($ $ktail) &no-effects)))
+ conts)))
+
+;; There is a way to abuse effects analysis in CSE to also do scalar
+;; replacement, effectively adding `car' and `cdr' expressions to `cons'
+;; expressions, and likewise with other constructors and setters. This
+;; routine adds appropriate effects to `cons' and `set-car!' and the
+;; like.
+;;
+;; This doesn't affect CSE's ability to eliminate expressions, given
+;; that allocations aren't eliminated anyway, and the new effects will
+;; just cause the allocations not to commute with e.g. set-car! which
+;; is what we want anyway.
+(define (synthesize-definition-effects effects)
+ (intmap-map (lambda (label fx)
+ (if (logtest (logior &write &allocation) fx)
+ (logior fx &read)
+ fx))
+ effects))
diff --git a/module/language/cps/elide-values.scm b/module/language/cps/elide-values.scm
new file mode 100644
index 000000000..81ccfc200
--- /dev/null
+++ b/module/language/cps/elide-values.scm
@@ -0,0 +1,88 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Primcalls that don't correspond to VM instructions are treated as if
+;;; they are calls, and indeed the later reify-primitives pass turns
+;;; them into calls. Because no return arity checking is done for these
+;;; primitives, if a later optimization pass simplifies the primcall to
+;;; a VM operation, the tail of the simplification has to be a
+;;; primcall to 'values. Most of these primcalls can be elided, and
+;;; that is the job of this pass.
+;;;
+;;; Code:
+
+(define-module (language cps elide-values)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:export (elide-values))
+
+(define (inline-values cps k src args)
+ (match (intmap-ref cps k)
+ (($ $ktail)
+ (with-cps cps
+ (build-term
+ ($continue k src ($values args)))))
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (cond
+ ((and (not rest) (= (length args) (length req)))
+ (with-cps cps
+ (build-term
+ ($continue kargs src ($values args)))))
+ ((and rest (>= (length args) (length req)))
+ (let ()
+ (define (build-rest cps k tail)
+ (match tail
+ (()
+ (with-cps cps
+ (build-term ($continue k src ($const '())))))
+ ((v . tail)
+ (with-cps cps
+ (letv rest)
+ (letk krest ($kargs ('rest) (rest)
+ ($continue k src ($primcall 'cons (v rest)))))
+ ($ (build-rest krest tail))))))
+ (with-cps cps
+ (letv rest)
+ (letk krest ($kargs ('rest) (rest)
+ ($continue kargs src
+ ($values ,(append (list-head args (length req))
+ (list rest))))))
+ ($ (build-rest krest (list-tail args (length req)))))))
+ (else (with-cps cps #f))))))
+
+(define (elide-values conts)
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs names vars ($ $continue k src ($ $primcall 'values args)))
+ (call-with-values (lambda () (inline-values out k src args))
+ (lambda (out term)
+ (if term
+ (let ((cont (build-cont ($kargs names vars ,term))))
+ (intmap-replace! out label cont))
+ out))))
+ (_ out)))
+ conts
+ conts))))
diff --git a/module/language/cps/optimize.scm b/module/language/cps/optimize.scm
new file mode 100644
index 000000000..83a3f2dfe
--- /dev/null
+++ b/module/language/cps/optimize.scm
@@ -0,0 +1,106 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Optimizations on CPS.
+;;;
+;;; Code:
+
+(define-module (language cps optimize)
+ #:use-module (ice-9 match)
+ #:use-module (language cps constructors)
+ #:use-module (language cps contification)
+ #:use-module (language cps cse)
+ #:use-module (language cps dce)
+ #:use-module (language cps elide-values)
+ #:use-module (language cps prune-top-level-scopes)
+ #:use-module (language cps prune-bailouts)
+ #:use-module (language cps self-references)
+ #:use-module (language cps simplify)
+ #:use-module (language cps specialize-primcalls)
+ #:use-module (language cps split-rec)
+ #:use-module (language cps type-fold)
+ #:use-module (language cps verify)
+ #:export (optimize-higher-order-cps
+ optimize-first-order-cps))
+
+(define (kw-arg-ref args kw default)
+ (match (memq kw args)
+ ((_ val . _) val)
+ (_ default)))
+
+(define *debug?* #f)
+
+(define (maybe-verify program)
+ (if *debug?*
+ (verify program)
+ program))
+
+(define-syntax-rule (define-optimizer optimize (pass kw default) ...)
+ (define* (optimize program #:optional (opts '()))
+ ;; This series of assignments to `program' used to be a series of
+ ;; let* bindings of `program', as you would imagine. In compiled
+ ;; code this is fine because the compiler is able to allocate all
+ ;; let*-bound variable to the same slot, which also means that the
+ ;; garbage collector doesn't have to retain so many copies of the
+ ;; term being optimized. However during bootstrap, the interpreter
+ ;; doesn't do this optimization, leading to excessive data retention
+ ;; as the terms are rewritten. To marginally improve bootstrap
+ ;; memory usage, here we use set! instead. The compiler should
+ ;; produce the same code in any case, though currently it does not
+ ;; because it doesn't do escape analysis on the box created for the
+ ;; set!.
+ (maybe-verify program)
+ (set! program
+ (if (kw-arg-ref opts kw default)
+ (maybe-verify (pass program))
+ program))
+ ...
+ (verify program)
+ program))
+
+;; Passes that are needed:
+;;
+;; * Abort contification: turning abort primcalls into continuation
+;; calls, and eliding prompts if possible.
+;;
+;; * Loop peeling. Unrolls the first round through a loop if the
+;; loop has effects that CSE can work on. Requires effects
+;; analysis. When run before CSE, loop peeling is the equivalent
+;; of loop-invariant code motion (LICM).
+;;
+(define-optimizer optimize-higher-order-cps
+ (split-rec #:split-rec? #t)
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (prune-top-level-scopes #:prune-top-level-scopes? #t)
+ (simplify #:simplify? #t)
+ (contify #:contify? #t)
+ (inline-constructors #:inline-constructors? #t)
+ (specialize-primcalls #:specialize-primcalls? #t)
+ (elide-values #:elide-values? #t)
+ (prune-bailouts #:prune-bailouts? #t)
+ (eliminate-common-subexpressions #:cse? #t)
+ (type-fold #:type-fold? #t)
+ (resolve-self-references #:resolve-self-references? #t)
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (simplify #:simplify? #t))
+
+(define-optimizer optimize-first-order-cps
+ (eliminate-dead-code #:eliminate-dead-code? #t)
+ (simplify #:simplify? #t))
diff --git a/module/language/cps/prune-bailouts.scm b/module/language/cps/prune-bailouts.scm
new file mode 100644
index 000000000..7c10319e8
--- /dev/null
+++ b/module/language/cps/prune-bailouts.scm
@@ -0,0 +1,86 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A pass that prunes successors of expressions that bail out.
+;;;
+;;; Code:
+
+(define-module (language cps prune-bailouts)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (prune-bailouts))
+
+(define (compute-tails conts)
+ "For each LABEL->CONT entry in the intmap CONTS, compute a
+LABEL->TAIL-LABEL indicating the tail continuation of each expression's
+containing function. In some cases TAIL-LABEL might not be available,
+for example if there is a stale $kfun pointing at a body, or for
+unreferenced terms. In that case TAIL-LABEL is either absent or #f."
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kfun src meta self tail clause)
+ (intset-fold (lambda (label out)
+ (intmap-add out label tail (lambda (old new) #f)))
+ (compute-function-body conts label)
+ out))
+ (_ out)))
+ conts
+ empty-intmap))
+
+(define (prune-bailout out tails k src exp)
+ (match (intmap-ref out k)
+ (($ $ktail)
+ (with-cps out #f))
+ (_
+ (match (intmap-ref tails k (lambda (_) #f))
+ (#f
+ (with-cps out #f))
+ (ktail
+ (with-cps out
+ (letv prim rest)
+ (letk kresult ($kargs ('rest) (rest)
+ ($continue ktail src ($values ()))))
+ (letk kreceive ($kreceive '() 'rest kresult))
+ (build-term ($continue kreceive src ,exp))))))))
+
+(define (prune-bailouts conts)
+ (let ((tails (compute-tails conts)))
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs names vars
+ ($ $continue k src
+ (and exp ($ $primcall (or 'error 'scm-error 'throw)))))
+ (call-with-values (lambda () (prune-bailout out tails k src exp))
+ (lambda (out term)
+ (if term
+ (let ((cont (build-cont ($kargs names vars ,term))))
+ (intmap-replace! out label cont))
+ out))))
+ (_ out)))
+ conts
+ conts)))))
diff --git a/module/language/cps/prune-top-level-scopes.scm b/module/language/cps/prune-top-level-scopes.scm
new file mode 100644
index 000000000..1970d1bc3
--- /dev/null
+++ b/module/language/cps/prune-top-level-scopes.scm
@@ -0,0 +1,63 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2014, 2015 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:
+;;;
+;;; A simple pass to prune unneeded top-level scopes.
+;;;
+;;; Code:
+
+(define-module (language cps prune-top-level-scopes)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (prune-top-level-scopes))
+
+(define (compute-used-scopes conts constants)
+ (persistent-intset
+ (intmap-fold
+ (lambda (label cont used-scopes)
+ (match cont
+ (($ $kargs _ _
+ ($ $continue k src
+ ($ $primcall 'cached-toplevel-box (scope name bound?))))
+ (intset-add! used-scopes (intmap-ref constants scope)))
+ (_
+ used-scopes)))
+ conts
+ empty-intset)))
+
+(define (prune-top-level-scopes conts)
+ (let* ((constants (compute-constant-values conts))
+ (used-scopes (compute-used-scopes conts constants)))
+ (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names vars
+ ($ $continue k src
+ ($ $primcall 'cache-current-module!
+ (module (? (lambda (scope)
+ (let ((val (intmap-ref constants scope)))
+ (not (intset-ref used-scopes val)))))))))
+ (build-cont ($kargs names vars
+ ($continue k src ($values ())))))
+ (_
+ cont)))
+ conts)))
diff --git a/module/language/cps/reify-primitives.scm b/module/language/cps/reify-primitives.scm
new file mode 100644
index 000000000..014593a9a
--- /dev/null
+++ b/module/language/cps/reify-primitives.scm
@@ -0,0 +1,167 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A pass to reify lone $prim's that were never folded into a
+;;; $primcall, and $primcall's to primitives that don't have a
+;;; corresponding VM op.
+;;;
+;;; Code:
+
+(define-module (language cps reify-primitives)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps primitives)
+ #:use-module (language cps intmap)
+ #:use-module (language bytecode)
+ #:export (reify-primitives))
+
+(define (module-box cps src module name public? bound? val-proc)
+ (with-cps cps
+ (letv box)
+ (let$ body (val-proc box))
+ (letk kbox ($kargs ('box) (box) ,body))
+ ($ (with-cps-constants ((module module)
+ (name name)
+ (public? public?)
+ (bound? bound?))
+ (build-term ($continue kbox src
+ ($primcall 'cached-module-box
+ (module name public? bound?))))))))
+
+(define (primitive-module name)
+ (case name
+ ((bytevector?
+ bytevector-length
+
+ bytevector-u8-ref bytevector-u8-set!
+ bytevector-s8-ref bytevector-s8-set!
+
+ bytevector-u16-ref bytevector-u16-set!
+ bytevector-u16-native-ref bytevector-u16-native-set!
+ bytevector-s16-ref bytevector-s16-set!
+ bytevector-s16-native-ref bytevector-s16-native-set!
+
+ bytevector-u32-ref bytevector-u32-set!
+ bytevector-u32-native-ref bytevector-u32-native-set!
+ bytevector-s32-ref bytevector-s32-set!
+ bytevector-s32-native-ref bytevector-s32-native-set!
+
+ bytevector-u64-ref bytevector-u64-set!
+ bytevector-u64-native-ref bytevector-u64-native-set!
+ bytevector-s64-ref bytevector-s64-set!
+ bytevector-s64-native-ref bytevector-s64-native-set!
+
+ bytevector-ieee-single-ref bytevector-ieee-single-set!
+ bytevector-ieee-single-native-ref bytevector-ieee-single-native-set!
+ bytevector-ieee-double-ref bytevector-ieee-double-set!
+ bytevector-ieee-double-native-ref bytevector-ieee-double-native-set!)
+ '(rnrs bytevectors))
+ ((class-of) '(oop goops))
+ (else '(guile))))
+
+(define (primitive-ref cps name k src)
+ (module-box cps src (primitive-module name) name #f #t
+ (lambda (cps box)
+ (with-cps cps
+ (build-term
+ ($continue k src ($primcall 'box-ref (box))))))))
+
+(define (builtin-ref cps idx k src)
+ (with-cps cps
+ ($ (with-cps-constants ((idx idx))
+ (build-term
+ ($continue k src ($primcall 'builtin-ref (idx))))))))
+
+(define (reify-clause cps ktail)
+ (with-cps cps
+ (letv throw)
+ (let$ throw-body
+ (with-cps-constants ((wna 'wrong-number-of-args)
+ (false #f)
+ (str "Wrong number of arguments")
+ (eol '()))
+ (build-term
+ ($continue ktail #f
+ ($call throw (wna false str eol false))))))
+ (letk kthrow ($kargs ('throw) (throw) ,throw-body))
+ (let$ body (primitive-ref 'throw kthrow #f))
+ (letk kbody ($kargs () () ,body))
+ (letk kclause ($kclause ('() '() #f '() #f) kbody #f))
+ kclause))
+
+;; A $kreceive continuation should have only one predecessor.
+(define (uniquify-receive cps k)
+ (match (intmap-ref cps k)
+ (($ $kreceive ($ $arity req () rest () #f) kargs)
+ (with-cps cps
+ (letk k ($kreceive req rest kargs))
+ k))
+ (_
+ (with-cps cps k))))
+
+(define (reify-primitives cps)
+ (define (visit-cont label cont cps)
+ (define (resolve-prim cps name k src)
+ (cond
+ ((builtin-name->index name)
+ => (lambda (idx) (builtin-ref cps idx k src)))
+ (else
+ (primitive-ref cps name k src))))
+ (match cont
+ (($ $kfun src meta self tail #f)
+ (with-cps cps
+ (let$ clause (reify-clause tail))
+ (setk label ($kfun src meta self tail clause))))
+ (($ $kargs names vars ($ $continue k src ($ $prim name)))
+ (with-cps cps
+ (let$ k (uniquify-receive k))
+ (let$ body (resolve-prim name k src))
+ (setk label ($kargs names vars ,body))))
+ (($ $kargs names vars
+ ($ $continue k src ($ $primcall 'call-thunk/no-inline (proc))))
+ (with-cps cps
+ (setk label ($kargs names vars ($continue k src ($call proc ()))))))
+ (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
+ (if (or (prim-instruction name) (branching-primitive? name))
+ ;; Assume arities are correct.
+ cps
+ (with-cps cps
+ (letv proc)
+ (let$ k (uniquify-receive k))
+ (letk kproc ($kargs ('proc) (proc)
+ ($continue k src ($call proc args))))
+ (let$ body (resolve-prim name kproc src))
+ (setk label ($kargs names vars ,body)))))
+ (($ $kargs names vars ($ $continue k src ($ $call proc args)))
+ (with-cps cps
+ (let$ k (uniquify-receive k))
+ (setk label ($kargs names vars
+ ($continue k src ($call proc args))))))
+ (($ $kargs names vars ($ $continue k src ($ $callk k* proc args)))
+ (with-cps cps
+ (let$ k (uniquify-receive k))
+ (setk label ($kargs names vars
+ ($continue k src ($callk k* proc args))))))
+ (_ cps)))
+
+ (with-fresh-name-state cps
+ (persistent-intmap (intmap-fold visit-cont cps cps))))
diff --git a/module/language/cps/renumber.scm b/module/language/cps/renumber.scm
new file mode 100644
index 000000000..8bab8634d
--- /dev/null
+++ b/module/language/cps/renumber.scm
@@ -0,0 +1,217 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A pass to renumber variables and continuation labels so that they
+;;; are contiguous within each function and, in the case of labels,
+;;; topologically sorted.
+;;;
+;;; Code:
+
+(define-module (language cps renumber)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intset)
+ #:use-module (language cps intmap)
+ #:export (renumber))
+
+(define* (compute-tail-path-lengths conts kfun preds)
+ (define (add-lengths labels lengths length)
+ (intset-fold (lambda (label lengths)
+ (intmap-add! lengths label length))
+ labels
+ lengths))
+ (define (compute-next labels lengths)
+ (intset-fold (lambda (label labels)
+ (fold1 (lambda (pred labels)
+ (if (intmap-ref lengths pred (lambda (_) #f))
+ labels
+ (intset-add! labels pred)))
+ (intmap-ref preds label)
+ labels))
+ labels
+ empty-intset))
+ (define (visit labels lengths length)
+ (let ((lengths (add-lengths labels lengths length)))
+ (values (compute-next labels lengths) lengths (1+ length))))
+ (match (intmap-ref conts kfun)
+ (($ $kfun src meta self tail clause)
+ (worklist-fold visit (intset-add empty-intset tail) empty-intmap 0))))
+
+;; Topologically sort the continuation tree starting at k0, using
+;; reverse post-order numbering.
+(define (sort-labels-locally conts k0 path-lengths)
+ (define (visit-kf-first? kf kt)
+ ;; Visit the successor of a branch with the shortest path length to
+ ;; the tail first, so that if the branches are unsorted, the longer
+ ;; path length will appear first. This will move a loop exit out of
+ ;; a loop.
+ (let ((kf-len (intmap-ref path-lengths kf (lambda (_) #f)))
+ (kt-len (intmap-ref path-lengths kt (lambda (_) #f))))
+ (if kt-len
+ (or (not kf-len) (< kf-len kt-len)
+ ;; If the path lengths are the same, preserve original
+ ;; order to avoid squirreliness.
+ (and (= kf-len kt-len) (< kt kf)))
+ (if kf-len #f (< kt kf)))))
+ (let ((order '())
+ (visited empty-intset))
+ (let visit ((k k0) (order '()) (visited empty-intset))
+ (define (visit2 k0 k1 order visited)
+ (let-values (((order visited) (visit k0 order visited)))
+ (visit k1 order visited)))
+ (if (intset-ref visited k)
+ (values order visited)
+ (let ((visited (intset-add visited k)))
+ (call-with-values
+ (lambda ()
+ (match (intmap-ref conts k)
+ (($ $kargs names syms ($ $continue k src exp))
+ (match exp
+ (($ $prompt escape? tag handler)
+ (visit2 k handler order visited))
+ (($ $branch kt)
+ (if (visit-kf-first? k kt)
+ (visit2 k kt order visited)
+ (visit2 kt k order visited)))
+ (_
+ (visit k order visited))))
+ (($ $kreceive arity k) (visit k order visited))
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (visit2 kalt kbody order visited)
+ (visit kbody order visited)))
+ (($ $kfun src meta self tail clause)
+ (if clause
+ (visit2 tail clause order visited)
+ (visit tail order visited)))
+ (($ $ktail) (values order visited))))
+ (lambda (order visited)
+ ;; Add k to the reverse post-order.
+ (values (cons k order) visited))))))))
+
+(define (compute-renaming conts kfun)
+ ;; labels := old -> new
+ ;; vars := old -> new
+ (define *next-label* -1)
+ (define *next-var* -1)
+ (define (rename-label label labels)
+ (set! *next-label* (1+ *next-label*))
+ (intmap-add! labels label *next-label*))
+ (define (rename-var sym vars)
+ (set! *next-var* (1+ *next-var*))
+ (intmap-add! vars sym *next-var*))
+ (define (rename label labels vars)
+ (values (rename-label label labels)
+ (match (intmap-ref conts label)
+ (($ $kargs names syms exp)
+ (fold1 rename-var syms vars))
+ (($ $kfun src meta self tail clause)
+ (rename-var self vars))
+ (_ vars))))
+ (define (maybe-visit-fun kfun labels vars)
+ (if (intmap-ref labels kfun (lambda (_) #f))
+ (values labels vars)
+ (visit-fun kfun labels vars)))
+ (define (visit-nested-funs k labels vars)
+ (match (intmap-ref conts k)
+ (($ $kargs names syms ($ $continue k src ($ $fun kfun)))
+ (visit-fun kfun labels vars))
+ (($ $kargs names syms ($ $continue k src ($ $rec names* syms*
+ (($ $fun kfun) ...))))
+ (fold2 visit-fun kfun labels vars))
+ (($ $kargs names syms ($ $continue k src ($ $closure kfun nfree)))
+ ;; Closures with zero free vars get copy-propagated so it's
+ ;; possible to already have visited them.
+ (maybe-visit-fun kfun labels vars))
+ (($ $kargs names syms ($ $continue k src ($ $callk kfun)))
+ ;; Well-known functions never have a $closure created for them
+ ;; and are only referenced by their $callk call sites.
+ (maybe-visit-fun kfun labels vars))
+ (_ (values labels vars))))
+ (define (visit-fun kfun labels vars)
+ (let* ((preds (compute-predecessors conts kfun))
+ (path-lengths (compute-tail-path-lengths conts kfun preds))
+ (order (sort-labels-locally conts kfun path-lengths)))
+ ;; First rename locally, then recurse on nested functions.
+ (let-values (((labels vars) (fold2 rename order labels vars)))
+ (fold2 visit-nested-funs order labels vars))))
+ (let-values (((labels vars) (visit-fun kfun empty-intmap empty-intmap)))
+ (values (persistent-intmap labels) (persistent-intmap vars))))
+
+(define* (renumber conts #:optional (kfun 0))
+ (let-values (((label-map var-map) (compute-renaming conts kfun)))
+ (define (rename-label label) (intmap-ref label-map label))
+ (define (rename-var var) (intmap-ref var-map var))
+ (define (rename-exp exp)
+ (rewrite-exp exp
+ ((or ($ $const) ($ $prim)) ,exp)
+ (($ $closure k nfree)
+ ($closure (rename-label k) nfree))
+ (($ $fun body)
+ ($fun (rename-label body)))
+ (($ $rec names vars funs)
+ ($rec names (map rename-var vars) (map rename-exp funs)))
+ (($ $values args)
+ ($values ,(map rename-var args)))
+ (($ $call proc args)
+ ($call (rename-var proc) ,(map rename-var args)))
+ (($ $callk k proc args)
+ ($callk (rename-label k) (rename-var proc) ,(map rename-var args)))
+ (($ $branch kt exp)
+ ($branch (rename-label kt) ,(rename-exp exp)))
+ (($ $primcall name args)
+ ($primcall name ,(map rename-var args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (rename-var tag) (rename-label handler)))))
+ (define (rename-arity arity)
+ (match arity
+ (($ $arity req opt rest () aok?)
+ arity)
+ (($ $arity req opt rest kw aok?)
+ (match kw
+ (() arity)
+ (((kw kw-name kw-var) ...)
+ (let ((kw (map list kw kw-name (map rename-var kw-var))))
+ (make-$arity req opt rest kw aok?)))))))
+ (persistent-intmap
+ (intmap-fold
+ (lambda (old-k new-k out)
+ (intmap-add!
+ out
+ new-k
+ (rewrite-cont (intmap-ref conts old-k)
+ (($ $kargs names syms ($ $continue k src exp))
+ ($kargs names (map rename-var syms)
+ ($continue (rename-label k) src ,(rename-exp exp))))
+ (($ $kreceive ($ $arity req () rest () #f) k)
+ ($kreceive req rest (rename-label k)))
+ (($ $ktail)
+ ($ktail))
+ (($ $kfun src meta self tail clause)
+ ($kfun src meta (rename-var self) (rename-label tail)
+ (and clause (rename-label clause))))
+ (($ $kclause arity body alternate)
+ ($kclause ,(rename-arity arity) (rename-label body)
+ (and alternate (rename-label alternate)))))))
+ label-map
+ empty-intmap))))
diff --git a/module/language/cps/self-references.scm b/module/language/cps/self-references.scm
new file mode 100644
index 000000000..cbdaaa107
--- /dev/null
+++ b/module/language/cps/self-references.scm
@@ -0,0 +1,79 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A pass that replaces free references to recursive functions with
+;;; bound references.
+;;;
+;;; Code:
+
+(define-module (language cps self-references)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (fold))
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (resolve-self-references))
+
+(define* (resolve-self-references cps #:optional (label 0) (env empty-intmap))
+ (define (subst var)
+ (intmap-ref env var (lambda (var) var)))
+
+ (define (rename-exp label cps names vars k src exp)
+ (let ((exp (rewrite-exp exp
+ ((or ($ $const) ($ $prim)) ,exp)
+ (($ $call proc args)
+ ($call (subst proc) ,(map subst args)))
+ (($ $callk k proc args)
+ ($callk k (subst proc) ,(map subst args)))
+ (($ $primcall name args)
+ ($primcall name ,(map subst args)))
+ (($ $branch k ($ $values (arg)))
+ ($branch k ($values ((subst arg)))))
+ (($ $branch k ($ $primcall name args))
+ ($branch k ($primcall name ,(map subst args))))
+ (($ $values args)
+ ($values ,(map subst args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (subst tag) handler)))))
+ (intmap-replace! cps label
+ (build-cont
+ ($kargs names vars ($continue k src ,exp))))))
+
+ (define (visit-exp cps label names vars k src exp)
+ (match exp
+ (($ $fun label)
+ (resolve-self-references cps label env))
+ (($ $rec names vars (($ $fun labels) ...))
+ (fold (lambda (label var cps)
+ (match (intmap-ref cps label)
+ (($ $kfun src meta self)
+ (resolve-self-references cps label
+ (intmap-add env var self)))))
+ cps labels vars))
+ (_ (rename-exp label cps names vars k src exp))))
+
+ (intset-fold (lambda (label cps)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-exp cps label names vars k src exp))
+ (_ cps)))
+ (compute-function-body cps label)
+ cps))
diff --git a/module/language/cps/simplify.scm b/module/language/cps/simplify.scm
new file mode 100644
index 000000000..a53bdbff6
--- /dev/null
+++ b/module/language/cps/simplify.scm
@@ -0,0 +1,267 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; The fundamental lambda calculus reductions, like beta and eta
+;;; reduction and so on. Pretty lame currently.
+;;;
+;;; Code:
+
+(define-module (language cps simplify)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-26)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intset)
+ #:use-module (language cps intmap)
+ #:export (simplify))
+
+(define (intset-maybe-add! set k add?)
+ (if add? (intset-add! set k) set))
+
+(define (intset-add* set k*)
+ (let lp ((set set) (k* k*))
+ (match k*
+ ((k . k*) (lp (intset-add set k) k*))
+ (() set))))
+
+(define (intset-add*! set k*)
+ (fold1 (lambda (k set) (intset-add! set k)) k* set))
+
+(define (fold2* f l1 l2 seed)
+ (let lp ((l1 l1) (l2 l2) (seed seed))
+ (match (cons l1 l2)
+ ((() . ()) seed)
+ (((x1 . l1) . (x2 . l2)) (lp l1 l2 (f x1 x2 seed))))))
+
+(define (transform-conts f conts)
+ (persistent-intmap
+ (intmap-fold (lambda (k v out)
+ (let ((v* (f k v)))
+ (cond
+ ((equal? v v*) out)
+ (v* (intmap-replace! out k v*))
+ (else (intmap-remove out k)))))
+ conts
+ conts)))
+
+(define (compute-singly-referenced-vars conts)
+ (define (visit label cont single multiple)
+ (define (add-ref var single multiple)
+ (if (intset-ref single var)
+ (values single (intset-add! multiple var))
+ (values (intset-add! single var) multiple)))
+ (define (ref var) (add-ref var single multiple))
+ (define (ref* vars) (fold2 add-ref vars single multiple))
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
+ (values single multiple))
+ (($ $call proc args)
+ (ref* (cons proc args)))
+ (($ $callk k proc args)
+ (ref* (cons proc args)))
+ (($ $primcall name args)
+ (ref* args))
+ (($ $values args)
+ (ref* args))
+ (($ $branch kt ($ $values (var)))
+ (ref var))
+ (($ $branch kt ($ $primcall name args))
+ (ref* args))
+ (($ $prompt escape? tag handler)
+ (ref tag))))
+ (_
+ (values single multiple))))
+ (let*-values (((single multiple) (values empty-intset empty-intset))
+ ((single multiple) (intmap-fold visit conts single multiple)))
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple))))
+
+;;; Continuations whose values are simply forwarded to another and not
+;;; used in any other way may be elided via eta reduction over labels.
+;;;
+;;; There is an exception however: we must exclude strongly-connected
+;;; components (SCCs). The only kind of SCC we can build out of $values
+;;; expressions are infinite loops.
+;;;
+;;; Condition A below excludes single-node SCCs. Single-node SCCs
+;;; cannot be reduced.
+;;;
+;;; Condition B conservatively excludes edges to labels already marked
+;;; as candidates. This prevents back-edges and so breaks SCCs, and is
+;;; optimal if labels are sorted. If the labels aren't sorted it's
+;;; suboptimal but cheap.
+(define (compute-eta-reductions conts kfun)
+ (let ((singly-used (compute-singly-referenced-vars conts)))
+ (define (singly-used? vars)
+ (match vars
+ (() #t)
+ ((var . vars)
+ (and (intset-ref singly-used var) (singly-used? vars)))))
+ (define (visit-fun kfun body eta)
+ (define (visit-cont label eta)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src ($ $values vars)))
+ (intset-maybe-add! eta label
+ (match (intmap-ref conts k)
+ (($ $kargs)
+ (and (not (eqv? label k)) ; A
+ (not (intset-ref eta label)) ; B
+ (singly-used? vars)))
+ (_ #f))))
+ (_
+ eta)))
+ (intset-fold visit-cont body eta))
+ (persistent-intset
+ (intmap-fold visit-fun
+ (compute-reachable-functions conts kfun)
+ empty-intset))))
+
+(define (eta-reduce conts kfun)
+ (let ((label-set (compute-eta-reductions conts kfun)))
+ ;; Replace any continuation to a label in LABEL-SET with the label's
+ ;; continuation. The label will denote a $kargs continuation, so
+ ;; only terms that can continue to $kargs need be taken into
+ ;; account.
+ (define (subst label)
+ (if (intset-ref label-set label)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue k)) (subst k)))
+ label))
+ (transform-conts
+ (lambda (label cont)
+ (and (not (intset-ref label-set label))
+ (rewrite-cont cont
+ (($ $kargs names syms ($ $continue kf src ($ $branch kt exp)))
+ ($kargs names syms
+ ($continue (subst kf) src ($branch (subst kt) ,exp))))
+ (($ $kargs names syms ($ $continue k src exp))
+ ($kargs names syms
+ ($continue (subst k) src ,exp)))
+ (($ $kreceive ($ $arity req () rest () #f) k)
+ ($kreceive req rest (subst k)))
+ (($ $kclause arity body alt)
+ ($kclause ,arity (subst body) alt))
+ (_ ,cont))))
+ conts)))
+
+(define (compute-singly-referenced-labels conts body)
+ (define (add-ref label single multiple)
+ (define (ref k single multiple)
+ (if (intset-ref single k)
+ (values single (intset-add! multiple k))
+ (values (intset-add! single k) multiple)))
+ (define (ref0) (values single multiple))
+ (define (ref1 k) (ref k single multiple))
+ (define (ref2 k k*)
+ (if k*
+ (let-values (((single multiple) (ref k single multiple)))
+ (ref k* single multiple))
+ (ref1 k)))
+ (match (intmap-ref conts label)
+ (($ $kreceive arity k) (ref1 k))
+ (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
+ (($ $ktail) (ref0))
+ (($ $kclause arity kbody kalt) (ref2 kbody kalt))
+ (($ $kargs names syms ($ $continue k src exp))
+ (ref2 k (match exp (($ $branch k) k) (($ $prompt _ _ k) k) (_ #f))))))
+ (let*-values (((single multiple) (values empty-intset empty-intset))
+ ((single multiple) (intset-fold add-ref body single multiple)))
+ (intset-subtract (persistent-intset single)
+ (persistent-intset multiple))))
+
+(define (compute-beta-reductions conts kfun)
+ (define (visit-fun kfun body beta)
+ (let ((single (compute-singly-referenced-labels conts body)))
+ (define (visit-cont label beta)
+ (match (intmap-ref conts label)
+ ;; A continuation's body can be inlined in place of a $values
+ ;; expression if the continuation is a $kargs. It should only
+ ;; be inlined if it is used only once, and not recursively.
+ (($ $kargs _ _ ($ $continue k src ($ $values)))
+ (intset-maybe-add! beta label
+ (and (intset-ref single k)
+ (match (intmap-ref conts k)
+ (($ $kargs) #t)
+ (_ #f)))))
+ (_
+ beta)))
+ (intset-fold visit-cont body beta)))
+ (persistent-intset
+ (intmap-fold visit-fun
+ (compute-reachable-functions conts kfun)
+ empty-intset)))
+
+(define (compute-beta-var-substitutions conts label-set)
+ (define (add-var-substs label var-map)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue k _ ($ $values vals)))
+ (match (intmap-ref conts k)
+ (($ $kargs names vars)
+ (fold2* (lambda (var val var-map)
+ (intmap-add! var-map var val))
+ vars vals var-map))))))
+ (intset-fold add-var-substs label-set empty-intmap))
+
+(define (beta-reduce conts kfun)
+ (let* ((label-set (compute-beta-reductions conts kfun))
+ (var-map (compute-beta-var-substitutions conts label-set)))
+ (define (subst var)
+ (match (intmap-ref var-map var (lambda (_) #f))
+ (#f var)
+ (val (subst val))))
+ (define (transform-exp label k src exp)
+ (if (intset-ref label-set label)
+ (match (intmap-ref conts k)
+ (($ $kargs _ _ ($ $continue k* src* exp*))
+ (transform-exp k k* src* exp*)))
+ (build-term
+ ($continue k src
+ ,(rewrite-exp exp
+ ((or ($ $const) ($ $prim) ($ $fun) ($ $rec) ($ $closure))
+ ,exp)
+ (($ $call proc args)
+ ($call (subst proc) ,(map subst args)))
+ (($ $callk k proc args)
+ ($callk k (subst proc) ,(map subst args)))
+ (($ $primcall name args)
+ ($primcall name ,(map subst args)))
+ (($ $values args)
+ ($values ,(map subst args)))
+ (($ $branch kt ($ $values (var)))
+ ($branch kt ($values ((subst var)))))
+ (($ $branch kt ($ $primcall name args))
+ ($branch kt ($primcall name ,(map subst args))))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (subst tag) handler)))))))
+ (transform-conts
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names syms ($ $continue k src exp))
+ (build-cont
+ ($kargs names syms ,(transform-exp label k src exp))))
+ (_ cont)))
+ conts)))
+
+(define (simplify conts)
+ (eta-reduce (beta-reduce conts 0) 0))
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
new file mode 100644
index 000000000..74e71c487
--- /dev/null
+++ b/module/language/cps/slot-allocation.scm
@@ -0,0 +1,995 @@
+;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; A module to assign stack slots to variables in a CPS term.
+;;;
+;;; Code:
+
+(define-module (language cps slot-allocation)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-26)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (allocate-slots
+ lookup-slot
+ lookup-maybe-slot
+ lookup-constant-value
+ lookup-maybe-constant-value
+ lookup-nlocals
+ lookup-call-proc-slot
+ lookup-parallel-moves
+ lookup-dead-slot-map))
+
+(define-record-type $allocation
+ (make-allocation slots constant-values call-allocs shuffles frame-sizes)
+ allocation?
+
+ ;; A map of VAR to slot allocation. A slot allocation is an integer,
+ ;; if the variable has been assigned a slot.
+ ;;
+ (slots allocation-slots)
+
+ ;; A map of VAR to constant value, for variables with constant values.
+ ;;
+ (constant-values allocation-constant-values)
+
+ ;; A map of LABEL to /call allocs/, for expressions that continue to
+ ;; $kreceive continuations: non-tail calls and $prompt expressions.
+ ;;
+ ;; A call alloc contains two pieces of information: the call's /proc
+ ;; slot/ and a /dead slot map/. The proc slot indicates the slot of a
+ ;; procedure in a procedure call, or where the procedure would be in a
+ ;; multiple-value return.
+ ;;
+ ;; The dead slot map indicates, what slots should be ignored by GC
+ ;; when marking the frame. A dead slot map is a bitfield, as an
+ ;; integer.
+ ;;
+ (call-allocs allocation-call-allocs)
+
+ ;; A map of LABEL to /parallel moves/. Parallel moves shuffle locals
+ ;; into position for a $call, $callk, or $values, or shuffle returned
+ ;; values back into place in a $kreceive.
+ ;;
+ ;; A set of moves is expressed as an ordered list of (SRC . DST)
+ ;; moves, where SRC and DST are slots. This may involve a temporary
+ ;; variable.
+ ;;
+ (shuffles allocation-shuffles)
+
+ ;; The number of locals for a $kclause.
+ ;;
+ (frame-sizes allocation-frame-sizes))
+
+(define-record-type $call-alloc
+ (make-call-alloc proc-slot dead-slot-map)
+ call-alloc?
+ (proc-slot call-alloc-proc-slot)
+ (dead-slot-map call-alloc-dead-slot-map))
+
+(define (lookup-maybe-slot var allocation)
+ (intmap-ref (allocation-slots allocation) var (lambda (_) #f)))
+
+(define (lookup-slot var allocation)
+ (intmap-ref (allocation-slots allocation) var))
+
+(define *absent* (list 'absent))
+
+(define (lookup-constant-value var allocation)
+ (let ((value (intmap-ref (allocation-constant-values allocation) var
+ (lambda (_) *absent*))))
+ (when (eq? value *absent*)
+ (error "Variable does not have constant value" var))
+ value))
+
+(define (lookup-maybe-constant-value var allocation)
+ (let ((value (intmap-ref (allocation-constant-values allocation) var
+ (lambda (_) *absent*))))
+ (if (eq? value *absent*)
+ (values #f #f)
+ (values #t value))))
+
+(define (lookup-call-alloc k allocation)
+ (intmap-ref (allocation-call-allocs allocation) k))
+
+(define (lookup-call-proc-slot k allocation)
+ (or (call-alloc-proc-slot (lookup-call-alloc k allocation))
+ (error "Call has no proc slot" k)))
+
+(define (lookup-parallel-moves k allocation)
+ (intmap-ref (allocation-shuffles allocation) k))
+
+(define (lookup-dead-slot-map k allocation)
+ (or (call-alloc-dead-slot-map (lookup-call-alloc k allocation))
+ (error "Call has no dead slot map" k)))
+
+(define (lookup-nlocals k allocation)
+ (intmap-ref (allocation-frame-sizes allocation) k))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define (solve-flow-equations succs in out kill gen subtract add meet)
+ "Find a fixed point for flow equations for SUCCS, where IN and OUT are
+the initial conditions as intmaps with one key for every node in SUCCS.
+KILL and GEN are intmaps indicating the state that is killed or defined
+at every node, and SUBTRACT, ADD, and MEET operates on that state."
+ (define (visit label in out)
+ (let* ((in-1 (intmap-ref in label))
+ (kill-1 (intmap-ref kill label))
+ (gen-1 (intmap-ref gen label))
+ (out-1 (intmap-ref out label))
+ (out-1* (add (subtract in-1 kill-1) gen-1)))
+ (if (eq? out-1 out-1*)
+ (values empty-intset in out)
+ (let ((out (intmap-replace! out label out-1*)))
+ (call-with-values
+ (lambda ()
+ (intset-fold (lambda (succ in changed)
+ (let* ((in-1 (intmap-ref in succ))
+ (in-1* (meet in-1 out-1*)))
+ (if (eq? in-1 in-1*)
+ (values in changed)
+ (values (intmap-replace! in succ in-1*)
+ (intset-add changed succ)))))
+ (intmap-ref succs label) in empty-intset))
+ (lambda (in changed)
+ (values changed in out)))))))
+
+ (let run ((worklist (intmap-keys succs)) (in in) (out out))
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist popped)
+ (if popped
+ (call-with-values (lambda () (visit popped in out))
+ (lambda (changed in out)
+ (run (intset-union worklist changed) in out)))
+ (values (persistent-intmap in)
+ (persistent-intmap out)))))))
+
+(define-syntax-rule (persistent-intmap2 exp)
+ (call-with-values (lambda () exp)
+ (lambda (a b)
+ (values (persistent-intmap a) (persistent-intmap b)))))
+
+(define (compute-defs-and-uses cps)
+ "Return two LABEL->VAR... maps indicating values defined at and used
+by a label, respectively."
+ (define (vars->intset vars)
+ (fold (lambda (var set) (intset-add set var)) empty-intset vars))
+ (persistent-intmap2
+ (intmap-fold
+ (lambda (label cont defs uses)
+ (define (get-defs k)
+ (match (intmap-ref cps k)
+ (($ $kargs names vars) (vars->intset vars))
+ (_ empty-intset)))
+ (define (return d u)
+ (values (intmap-add! defs label d)
+ (intmap-add! uses label u)))
+ (match cont
+ (($ $kfun src meta self)
+ (return (intset self) empty-intset))
+ (($ $kargs _ _ ($ $continue k src exp))
+ (match exp
+ ((or ($ $const) ($ $closure))
+ (return (get-defs k) empty-intset))
+ (($ $call proc args)
+ (return (get-defs k) (intset-add (vars->intset args) proc)))
+ (($ $callk _ proc args)
+ (return (get-defs k) (intset-add (vars->intset args) proc)))
+ (($ $primcall name args)
+ (return (get-defs k) (vars->intset args)))
+ (($ $branch kt ($ $primcall name args))
+ (return empty-intset (vars->intset args)))
+ (($ $branch kt ($ $values args))
+ (return empty-intset (vars->intset args)))
+ (($ $values args)
+ (return (get-defs k) (vars->intset args)))
+ (($ $prompt escape? tag handler)
+ (return empty-intset (intset tag)))))
+ (($ $kclause arity body alt)
+ (return (get-defs body) empty-intset))
+ (($ $kreceive arity kargs)
+ (return (get-defs kargs) empty-intset))
+ (($ $ktail)
+ (return empty-intset empty-intset))))
+ cps
+ empty-intmap
+ empty-intmap)))
+
+(define (compute-reverse-control-flow-order preds)
+ "Return a LABEL->ORDER bijection where ORDER is a contiguous set of
+integers starting from 0 and incrementing in sort order."
+ ;; This is more involved than forward control flow because not all
+ ;; live labels are reachable from the tail.
+ (persistent-intmap
+ (fold2 (lambda (component order n)
+ (intset-fold (lambda (label order n)
+ (values (intmap-add! order label n)
+ (1+ n)))
+ component order n))
+ (reverse (compute-sorted-strongly-connected-components preds))
+ empty-intmap 0)))
+
+(define* (add-prompt-control-flow-edges conts succs #:key complete?)
+ "For all prompts in DFG in the range [MIN-LABEL, MIN-LABEL +
+LABEL-COUNT), invoke F with arguments PROMPT, HANDLER, and BODY for each
+body continuation in the prompt."
+ (define (intset-filter pred set)
+ (intset-fold (lambda (i set)
+ (if (pred i) set (intset-remove set i)))
+ set
+ set))
+ (define (intset-any pred set)
+ (intset-fold (lambda (i res)
+ (if (or res (pred i)) #t res))
+ set
+ #f))
+ (define (visit-prompt label handler succs)
+ ;; FIXME: It isn't correct to use all continuations reachable from
+ ;; the prompt, because that includes continuations outside the
+ ;; prompt body. This point is moot if the handler's control flow
+ ;; joins with the the body, as is usually but not always the case.
+ ;;
+ ;; One counter-example is when the handler contifies an infinite
+ ;; loop; in that case we compute a too-large prompt body. This
+ ;; error is currently innocuous, but we should fix it at some point.
+ ;;
+ ;; The fix is to end the body at the corresponding "pop" primcall,
+ ;; if any.
+ (let ((body (intset-subtract (compute-function-body conts label)
+ (compute-function-body conts handler))))
+ (define (out-or-back-edge? label)
+ ;; Most uses of visit-prompt-control-flow don't need every body
+ ;; continuation, and would be happy getting called only for
+ ;; continuations that postdominate the rest of the body. Unless
+ ;; you pass #:complete? #t, we only invoke F on continuations
+ ;; that can leave the body, or on back-edges in loops.
+ ;;
+ ;; You would think that looking for the final "pop" primcall
+ ;; would be sufficient, but that is incorrect; it's possible for
+ ;; a loop in the prompt body to be contified, and that loop need
+ ;; not continue to the pop if it never terminates. The pop could
+ ;; even be removed by DCE, in that case.
+ (intset-any (lambda (succ)
+ (or (not (intset-ref body succ))
+ (<= succ label)))
+ (intmap-ref succs label)))
+ (intset-fold (lambda (pred succs)
+ (intmap-replace succs pred handler intset-add))
+ (if complete? body (intset-filter out-or-back-edge? body))
+ succs)))
+ (intmap-fold
+ (lambda (label cont succs)
+ (match cont
+ (($ $kargs _ _
+ ($ $continue _ _ ($ $prompt escape? tag handler)))
+ (visit-prompt label handler succs))
+ (_ succs)))
+ conts
+ succs))
+
+(define (rename-keys map old->new)
+ (persistent-intmap
+ (intmap-fold (lambda (k v out)
+ (intmap-add! out (intmap-ref old->new k) v))
+ map
+ empty-intmap)))
+
+(define (rename-intset set old->new)
+ (intset-fold (lambda (old set) (intset-add set (intmap-ref old->new old)))
+ set empty-intset))
+
+(define (rename-graph graph old->new)
+ (persistent-intmap
+ (intmap-fold (lambda (pred succs out)
+ (intmap-add! out
+ (intmap-ref old->new pred)
+ (rename-intset succs old->new)))
+ graph
+ empty-intmap)))
+
+(define (compute-live-variables cps defs uses)
+ "Compute and return two values mapping LABEL->VAR..., where VAR... are
+the definitions that are live before and after LABEL, as intsets."
+ (let* ((succs (add-prompt-control-flow-edges cps (compute-successors cps)))
+ (preds (invert-graph succs))
+ (old->new (compute-reverse-control-flow-order preds)))
+ (call-with-values
+ (lambda ()
+ (let ((init (rename-keys
+ (intmap-map (lambda (k v) empty-intset) preds)
+ old->new)))
+ (solve-flow-equations (rename-graph preds old->new)
+ init init
+ (rename-keys defs old->new)
+ (rename-keys uses old->new)
+ intset-subtract intset-union intset-union)))
+ (lambda (in out)
+ ;; As a reverse control-flow problem, the values flowing into a
+ ;; node are actually the live values after the node executes.
+ ;; Funny, innit? So we return them in the reverse order.
+ (let ((new->old (invert-bijection old->new)))
+ (values (rename-keys out new->old)
+ (rename-keys in new->old)))))))
+
+(define (compute-needs-slot cps defs uses)
+ (define (get-defs k) (intmap-ref defs k))
+ (define (get-uses label) (intmap-ref uses label))
+ (intmap-fold
+ (lambda (label cont needs-slot)
+ (intset-union
+ needs-slot
+ (match cont
+ (($ $kargs _ _ ($ $continue k src exp))
+ (let ((defs (get-defs label)))
+ (define (defs+* uses)
+ (intset-union defs uses))
+ (define (defs+ use)
+ (intset-add defs use))
+ (match exp
+ (($ $const)
+ empty-intset)
+ (($ $primcall 'free-ref (closure slot))
+ (defs+ closure))
+ (($ $primcall 'free-set! (closure slot value))
+ (defs+* (intset closure value)))
+ (($ $primcall 'cache-current-module! (mod . _))
+ (defs+ mod))
+ (($ $primcall 'cached-toplevel-box _)
+ defs)
+ (($ $primcall 'cached-module-box _)
+ defs)
+ (($ $primcall 'resolve (name bound?))
+ (defs+ name))
+ (($ $primcall 'make-vector/immediate (len init))
+ (defs+ init))
+ (($ $primcall 'vector-ref/immediate (v i))
+ (defs+ v))
+ (($ $primcall 'vector-set!/immediate (v i x))
+ (defs+* (intset v x)))
+ (($ $primcall 'allocate-struct/immediate (vtable nfields))
+ (defs+ vtable))
+ (($ $primcall 'struct-ref/immediate (s n))
+ (defs+ s))
+ (($ $primcall 'struct-set!/immediate (s n x))
+ (defs+* (intset s x)))
+ (($ $primcall 'builtin-ref (idx))
+ defs)
+ (_
+ (defs+* (get-uses label))))))
+ (($ $kreceive arity k)
+ ;; Only allocate results of function calls to slots if they are
+ ;; used.
+ empty-intset)
+ (($ $kclause arity body alternate)
+ (get-defs label))
+ (($ $kfun src meta self)
+ (intset self))
+ (($ $ktail)
+ empty-intset))))
+ cps
+ empty-intset))
+
+(define (compute-lazy-vars cps live-in live-out defs needs-slot)
+ "Compute and return a set of vars whose allocation can be delayed
+until their use is seen. These are \"lazy\" vars. A var is lazy if its
+uses are calls, it is always dead after the calls, and if the uses flow
+to the definition. A flow continues across a node iff the node kills no
+values that need slots, and defines only lazy vars. Calls also kill
+flows; there's no sense in trying to juggle a pending frame while there
+is an active call."
+ (define (list->intset list)
+ (persistent-intset
+ (fold (lambda (i set) (intset-add! set i)) empty-intset list)))
+
+ (let* ((succs (compute-successors cps))
+ (gens (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ ($ $call proc args)))
+ (intset-subtract (intset-add (list->intset args) proc)
+ (intmap-ref live-out label)))
+ (($ $kargs _ _ ($ $continue _ _ ($ $callk _ proc args)))
+ (intset-subtract (intset-add (list->intset args) proc)
+ (intmap-ref live-out label)))
+ (_ #f)))
+ cps))
+ (kills (intmap-map
+ (lambda (label in)
+ (let* ((out (intmap-ref live-out label))
+ (killed (intset-subtract in out))
+ (killed-slots (intset-intersect killed needs-slot)))
+ (and (eq? killed-slots empty-intset)
+ ;; Kill output variables that need slots.
+ (intset-intersect (intmap-ref defs label)
+ needs-slot))))
+ live-in))
+ (preds (invert-graph succs))
+ (old->new (compute-reverse-control-flow-order preds)))
+ (define (subtract lazy kill)
+ (cond
+ ((eq? lazy empty-intset)
+ lazy)
+ ((not kill)
+ empty-intset)
+ ((and lazy (eq? empty-intset (intset-subtract kill lazy)))
+ (intset-subtract lazy kill))
+ (else
+ empty-intset)))
+ (define (add live gen) (or gen live))
+ (define (meet in out)
+ ;; Initial in is #f.
+ (if in (intset-intersect in out) out))
+ (call-with-values
+ (lambda ()
+ (let ((succs (rename-graph preds old->new))
+ (in (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
+ (out (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
+ ;(out (rename-keys gens old->new))
+ (kills (rename-keys kills old->new))
+ (gens (rename-keys gens old->new)))
+ (solve-flow-equations succs in out kills gens subtract add meet)))
+ (lambda (in out)
+ ;; A variable is lazy if its uses reach its definition.
+ (intmap-fold (lambda (label out lazy)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars)
+ (let ((defs (list->intset vars)))
+ (intset-union lazy (intset-intersect out defs))))
+ (_ lazy)))
+ (rename-keys out (invert-bijection old->new))
+ empty-intset)))))
+
+(define (find-first-zero n)
+ ;; Naive implementation.
+ (let lp ((slot 0))
+ (if (logbit? slot n)
+ (lp (1+ slot))
+ slot)))
+
+(define (find-first-trailing-zero n)
+ (let lp ((slot (let lp ((count 2))
+ (if (< n (ash 1 (1- count)))
+ count
+ ;; Grow upper bound slower than factor 2 to avoid
+ ;; needless bignum allocation on 32-bit systems
+ ;; when there are more than 16 locals.
+ (lp (+ count (ash count -1)))))))
+ (if (or (zero? slot) (logbit? (1- slot) n))
+ slot
+ (lp (1- slot)))))
+
+(define (integers from count)
+ (if (zero? count)
+ '()
+ (cons from (integers (1+ from) (1- count)))))
+
+(define (solve-parallel-move src dst tmp)
+ "Solve the parallel move problem between src and dst slot lists, which
+are comparable with eqv?. A tmp slot may be used."
+
+ ;; This algorithm is taken from: "Tilting at windmills with Coq:
+ ;; formal verification of a compilation algorithm for parallel moves"
+ ;; by Laurence Rideau, Bernard Paul Serpette, and Xavier Leroy
+ ;;
+
+ (define (split-move moves reg)
+ (let loop ((revhead '()) (tail moves))
+ (match tail
+ (((and s+d (s . d)) . rest)
+ (if (eqv? s reg)
+ (cons d (append-reverse revhead rest))
+ (loop (cons s+d revhead) rest)))
+ (_ #f))))
+
+ (define (replace-last-source reg moves)
+ (match moves
+ ((moves ... (s . d))
+ (append moves (list (cons reg d))))))
+
+ (let loop ((to-move (map cons src dst))
+ (being-moved '())
+ (moved '())
+ (last-source #f))
+ ;; 'last-source' should always be equivalent to:
+ ;; (and (pair? being-moved) (car (last being-moved)))
+ (match being-moved
+ (() (match to-move
+ (() (reverse moved))
+ (((and s+d (s . d)) . t1)
+ (if (or (eqv? s d) ; idempotent
+ (not s)) ; src is a constant and can be loaded directly
+ (loop t1 '() moved #f)
+ (loop t1 (list s+d) moved s)))))
+ (((and s+d (s . d)) . b)
+ (match (split-move to-move d)
+ ((r . t1) (loop t1 (acons d r being-moved) moved last-source))
+ (#f (match b
+ (() (loop to-move '() (cons s+d moved) #f))
+ (_ (if (eqv? d last-source)
+ (loop to-move
+ (replace-last-source tmp b)
+ (cons s+d (acons d tmp moved))
+ tmp)
+ (loop to-move b (cons s+d moved) last-source))))))))))
+
+(define (compute-shuffles cps slots call-allocs live-in)
+ (define (add-live-slot slot live-slots)
+ (logior live-slots (ash 1 slot)))
+
+ (define (get-cont label)
+ (intmap-ref cps label))
+
+ (define (get-slot var)
+ (intmap-ref slots var (lambda (_) #f)))
+
+ (define (get-slots vars)
+ (let lp ((vars vars))
+ (match vars
+ ((var . vars) (cons (get-slot var) (lp vars)))
+ (_ '()))))
+
+ (define (get-proc-slot label)
+ (call-alloc-proc-slot (intmap-ref call-allocs label)))
+
+ (define (compute-live-slots label)
+ (intset-fold (lambda (var live)
+ (match (get-slot var)
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-in label)
+ 0))
+
+ ;; Although some parallel moves may proceed without a temporary slot,
+ ;; in general one is needed. That temporary slot must not be part of
+ ;; the source or destination sets, and that slot should not correspond
+ ;; to a live variable. Usually the source and destination sets are a
+ ;; subset of the union of the live sets before and after the move.
+ ;; However for stack slots that don't have names -- those slots that
+ ;; correspond to function arguments or to function return values -- it
+ ;; could be that they are out of the computed live set. In that case
+ ;; they need to be adjoined to the live set, used when choosing a
+ ;; temporary slot.
+ ;;
+ ;; Note that although we reserve slots 253-255 for shuffling operands
+ ;; that address less than the full 24-bit range of locals, that
+ ;; reservation doesn't apply here, because this temporary itself is
+ ;; used while doing parallel assignment via "mov", and "mov" does not
+ ;; need shuffling.
+ (define (compute-tmp-slot live stack-slots)
+ (find-first-zero (fold add-live-slot live stack-slots)))
+
+ (define (parallel-move src-slots dst-slots tmp-slot)
+ (solve-parallel-move src-slots dst-slots tmp-slot))
+
+ (define (compute-receive-shuffles label proc-slot)
+ (match (get-cont label)
+ (($ $kreceive arity kargs)
+ (let* ((results (match (get-cont kargs)
+ (($ $kargs names vars) vars)))
+ (value-slots (integers (1+ proc-slot) (length results)))
+ (result-slots (get-slots results))
+ ;; Filter out unused results.
+ (value-slots (filter-map (lambda (val result) (and result val))
+ value-slots result-slots))
+ (result-slots (filter (lambda (x) x) result-slots))
+ (live (compute-live-slots kargs)))
+ (parallel-move value-slots
+ result-slots
+ (compute-tmp-slot live value-slots))))))
+
+ (define (add-call-shuffles label k args shuffles)
+ (match (get-cont k)
+ (($ $ktail)
+ (let* ((live (compute-live-slots label))
+ (tail-slots (integers 0 (length args)))
+ (moves (parallel-move (get-slots args)
+ tail-slots
+ (compute-tmp-slot live tail-slots))))
+ (intmap-add! shuffles label moves)))
+ (($ $kreceive)
+ (let* ((live (compute-live-slots label))
+ (proc-slot (get-proc-slot label))
+ (call-slots (integers proc-slot (length args)))
+ (arg-moves (parallel-move (get-slots args)
+ call-slots
+ (compute-tmp-slot live call-slots))))
+ (intmap-add! (intmap-add! shuffles label arg-moves)
+ k (compute-receive-shuffles k proc-slot))))))
+
+ (define (add-values-shuffles label k args shuffles)
+ (match (get-cont k)
+ (($ $ktail)
+ (let* ((live (compute-live-slots label))
+ (src-slots (get-slots args))
+ (dst-slots (integers 1 (length args)))
+ (moves (parallel-move src-slots dst-slots
+ (compute-tmp-slot live dst-slots))))
+ (intmap-add! shuffles label moves)))
+ (($ $kargs _ dst-vars)
+ (let* ((live (logior (compute-live-slots label)
+ (compute-live-slots k)))
+ (src-slots (get-slots args))
+ (dst-slots (get-slots dst-vars))
+ (moves (parallel-move src-slots dst-slots
+ (compute-tmp-slot live '()))))
+ (intmap-add! shuffles label moves)))))
+
+ (define (add-prompt-shuffles label k handler shuffles)
+ (intmap-add! shuffles handler
+ (compute-receive-shuffles handler (get-proc-slot label))))
+
+ (define (compute-shuffles label cont shuffles)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $call proc args)
+ (add-call-shuffles label k (cons proc args) shuffles))
+ (($ $callk _ proc args)
+ (add-call-shuffles label k (cons proc args) shuffles))
+ (($ $values args)
+ (add-values-shuffles label k args shuffles))
+ (($ $prompt escape? tag handler)
+ (add-prompt-shuffles label k handler shuffles))
+ (_ shuffles)))
+ (_ shuffles)))
+
+ (persistent-intmap
+ (intmap-fold compute-shuffles cps empty-intmap)))
+
+(define (compute-frame-sizes cps slots call-allocs shuffles)
+ ;; Minimum frame has one slot: the closure.
+ (define minimum-frame-size 1)
+ (define (get-shuffles label)
+ (intmap-ref shuffles label))
+ (define (get-proc-slot label)
+ (match (intmap-ref call-allocs label (lambda (_) #f))
+ (#f 0) ;; Tail call.
+ (($ $call-alloc proc-slot) proc-slot)))
+ (define (max-size var size)
+ (match (intmap-ref slots var (lambda (_) #f))
+ (#f size)
+ (slot (max size (1+ slot)))))
+ (define (max-size* vars size)
+ (fold max-size size vars))
+ (define (shuffle-size moves size)
+ (match moves
+ (() size)
+ (((src . dst) . moves)
+ (shuffle-size moves (max size (1+ src) (1+ dst))))))
+ (define (call-size label nargs size)
+ (shuffle-size (get-shuffles label)
+ (max (+ (get-proc-slot label) nargs) size)))
+ (define (measure-cont label cont frame-sizes clause size)
+ (match cont
+ (($ $kfun)
+ (values #f #f #f))
+ (($ $kclause)
+ (let ((frame-sizes (if clause
+ (intmap-add! frame-sizes clause size)
+ empty-intmap)))
+ (values frame-sizes label minimum-frame-size)))
+ (($ $kargs names vars ($ $continue k src exp))
+ (values frame-sizes clause
+ (let ((size (max-size* vars size)))
+ (match exp
+ (($ $call proc args)
+ (call-size label (1+ (length args)) size))
+ (($ $callk _ proc args)
+ (call-size label (1+ (length args)) size))
+ (($ $values args)
+ (shuffle-size (get-shuffles label) size))
+ (_ size)))))
+ (($ $kreceive)
+ (values frame-sizes clause
+ (shuffle-size (get-shuffles label) size)))
+ (($ $ktail)
+ (values (intmap-add! frame-sizes clause size) #f #f))))
+
+ (persistent-intmap (intmap-fold measure-cont cps #f #f #f)))
+
+(define (allocate-args cps)
+ (intmap-fold (lambda (label cont slots)
+ (match cont
+ (($ $kfun src meta self)
+ (intmap-add! slots self 0))
+ (($ $kclause arity body alt)
+ (match (intmap-ref cps body)
+ (($ $kargs names vars)
+ (let lp ((vars vars) (slots slots) (n 1))
+ (match vars
+ (() slots)
+ ((var . vars)
+ (let ((n (if (<= 253 n 255) 256 n)))
+ (lp vars
+ (intmap-add! slots var n)
+ (1+ n)))))))))
+ (_ slots)))
+ cps empty-intmap))
+
+(define-inlinable (add-live-slot slot live-slots)
+ (logior live-slots (ash 1 slot)))
+
+(define-inlinable (kill-dead-slot slot live-slots)
+ (logand live-slots (lognot (ash 1 slot))))
+
+(define-inlinable (compute-slot live-slots hint)
+ ;; Slots 253-255 are reserved for shuffling; see comments in
+ ;; assembler.scm.
+ (if (and hint (not (logbit? hint live-slots))
+ (or (< hint 253) (> hint 255)))
+ hint
+ (let ((slot (find-first-zero live-slots)))
+ (if (or (< slot 253) (> slot 255))
+ slot
+ (+ 256 (find-first-zero (ash live-slots -256)))))))
+
+(define (allocate-lazy-vars cps slots call-allocs live-in lazy)
+ (define (compute-live-slots slots label)
+ (intset-fold (lambda (var live)
+ (match (intmap-ref slots var (lambda (_) #f))
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-in label)
+ 0))
+
+ (define (allocate var hint slots live)
+ (match (and hint (intmap-ref slots var (lambda (_) #f)))
+ (#f (if (intset-ref lazy var)
+ (let ((slot (compute-slot live hint)))
+ (values (intmap-add! slots var slot)
+ (add-live-slot slot live)))
+ (values slots live)))
+ (slot (values slots (add-live-slot slot live)))))
+
+ (define (allocate* vars hints slots live)
+ (match (vector vars hints)
+ (#(() ()) slots)
+ (#((var . vars) (hint . hints))
+ (let-values (((slots live) (allocate var hint slots live)))
+ (allocate* vars hints slots live)))))
+
+ (define (get-proc-slot label)
+ (match (intmap-ref call-allocs label (lambda (_) #f))
+ (#f 0)
+ (call (call-alloc-proc-slot call))))
+
+ (define (allocate-call label args slots)
+ (allocate* args (integers (get-proc-slot label) (length args))
+ slots (compute-live-slots slots label)))
+
+ (define (allocate-values label k args slots)
+ (match (intmap-ref cps k)
+ (($ $ktail)
+ (allocate* args (integers 1 (length args))
+ slots (compute-live-slots slots label)))
+ (($ $kargs names vars)
+ (allocate* args
+ (map (cut intmap-ref slots <> (lambda (_) #f)) vars)
+ slots (compute-live-slots slots label)))))
+
+ (define (allocate-lazy label cont slots)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $call proc args)
+ (allocate-call label (cons proc args) slots))
+ (($ $callk _ proc args)
+ (allocate-call label (cons proc args) slots))
+ (($ $values args)
+ (allocate-values label k args slots))
+ (_ slots)))
+ (_
+ slots)))
+
+ ;; Sweep right to left to visit uses before definitions.
+ (persistent-intmap
+ (intmap-fold-right allocate-lazy cps slots)))
+
+(define (allocate-slots cps)
+ (let*-values (((defs uses) (compute-defs-and-uses cps))
+ ((live-in live-out) (compute-live-variables cps defs uses))
+ ((constants) (compute-constant-values cps))
+ ((needs-slot) (compute-needs-slot cps defs uses))
+ ((lazy) (compute-lazy-vars cps live-in live-out defs
+ needs-slot)))
+
+ (define (empty-live-slots)
+ #b0)
+
+ (define (compute-call-proc-slot live-slots)
+ (+ 2 (find-first-trailing-zero live-slots)))
+
+ (define (compute-prompt-handler-proc-slot live-slots)
+ (if (zero? live-slots)
+ 0
+ (1- (find-first-trailing-zero live-slots))))
+
+ (define (get-cont label)
+ (intmap-ref cps label))
+
+ (define (get-slot slots var)
+ (intmap-ref slots var (lambda (_) #f)))
+
+ (define (get-slots slots vars)
+ (let lp ((vars vars))
+ (match vars
+ ((var . vars) (cons (get-slot slots var) (lp vars)))
+ (_ '()))))
+
+ (define (compute-live-slots* slots label live-vars)
+ (intset-fold (lambda (var live)
+ (match (get-slot slots var)
+ (#f live)
+ (slot (add-live-slot slot live))))
+ (intmap-ref live-vars label)
+ 0))
+
+ (define (compute-live-in-slots slots label)
+ (compute-live-slots* slots label live-in))
+
+ (define (compute-live-out-slots slots label)
+ (compute-live-slots* slots label live-out))
+
+ (define (allocate var hint slots live)
+ (cond
+ ((not (intset-ref needs-slot var))
+ (values slots live))
+ ((get-slot slots var)
+ => (lambda (slot)
+ (values slots (add-live-slot slot live))))
+ ((and (not hint) (intset-ref lazy var))
+ (values slots live))
+ (else
+ (let ((slot (compute-slot live hint)))
+ (values (intmap-add! slots var slot)
+ (add-live-slot slot live))))))
+
+ (define (allocate* vars hints slots live)
+ (match (vector vars hints)
+ (#(() ()) (values slots live))
+ (#((var . vars) (hint . hints))
+ (call-with-values (lambda () (allocate var hint slots live))
+ (lambda (slots live)
+ (allocate* vars hints slots live))))))
+
+ (define (allocate-defs label vars slots)
+ (let ((live (compute-live-in-slots slots label))
+ (live-vars (intmap-ref live-in label)))
+ (let lp ((vars vars) (slots slots) (live live))
+ (match vars
+ (() (values slots live))
+ ((var . vars)
+ (call-with-values (lambda () (allocate var #f slots live))
+ (lambda (slots live)
+ (lp vars slots
+ (let ((slot (get-slot slots var)))
+ (if (and slot (not (intset-ref live-vars var)))
+ (kill-dead-slot slot live)
+ live))))))))))
+
+ ;; PRE-LIVE are the live slots coming into the term. POST-LIVE
+ ;; is the subset of PRE-LIVE that is still live after the term
+ ;; uses its inputs.
+ (define (allocate-call label k args slots call-allocs pre-live)
+ (match (get-cont k)
+ (($ $ktail)
+ (let ((tail-slots (integers 0 (length args))))
+ (values (allocate* args tail-slots slots pre-live)
+ call-allocs)))
+ (($ $kreceive arity kargs)
+ (let*-values
+ (((post-live) (compute-live-out-slots slots label))
+ ((proc-slot) (compute-call-proc-slot post-live))
+ ((call-slots) (integers proc-slot (length args)))
+ ((slots pre-live) (allocate* args call-slots slots pre-live))
+ ;; Allow the first result to be hinted by its use, but
+ ;; hint the remaining results to stay in place. This
+ ;; strikes a balance between avoiding shuffling,
+ ;; especially for unused extra values, and avoiding frame
+ ;; size growth due to sparse locals.
+ ((slots result-live)
+ (match (get-cont kargs)
+ (($ $kargs () ())
+ (values slots post-live))
+ (($ $kargs (_ . _) (_ . results))
+ (let ((result-slots (integers (+ proc-slot 2)
+ (length results))))
+ (allocate* results result-slots slots post-live)))))
+ ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
+ (lognot post-live)))
+ ((call) (make-call-alloc proc-slot dead-slot-map)))
+ (values slots
+ (intmap-add! call-allocs label call))))))
+
+ (define (allocate-values label k args slots call-allocs)
+ (match (get-cont k)
+ (($ $ktail)
+ (values slots call-allocs))
+ (($ $kargs (_) (dst))
+ ;; When there is only one value in play, we allow the dst to be
+ ;; hinted (see compute-lazy-vars). If the src doesn't have a
+ ;; slot, then the actual slot for the dst would end up being
+ ;; decided by the call that args it. Because we don't know the
+ ;; slot, we can't really compute the parallel moves in that
+ ;; case, so just bail and rely on the bytecode emitter to
+ ;; handle the one-value case specially.
+ (match args
+ ((src)
+ (let ((post-live (compute-live-out-slots slots label)))
+ (values (allocate dst (get-slot slots src) slots post-live)
+ call-allocs)))))
+ (($ $kargs _ dst-vars)
+ (let ((src-slots (get-slots slots args))
+ (post-live (compute-live-out-slots slots label)))
+ (values (allocate* dst-vars src-slots slots post-live)
+ call-allocs)))))
+
+ (define (allocate-prompt label k handler slots call-allocs)
+ (match (get-cont handler)
+ (($ $kreceive arity kargs)
+ (let*-values
+ (((handler-live) (compute-live-in-slots slots handler))
+ ((proc-slot) (compute-prompt-handler-proc-slot handler-live))
+ ((dead-slot-map) (logand (1- (ash 1 (- proc-slot 2)))
+ (lognot handler-live)))
+ ((result-vars) (match (get-cont kargs)
+ (($ $kargs names vars) vars)))
+ ((value-slots) (integers (1+ proc-slot) (length result-vars)))
+ ((slots result-live) (allocate* result-vars value-slots
+ slots handler-live)))
+ (values slots
+ (intmap-add! call-allocs label
+ (make-call-alloc proc-slot dead-slot-map)))))))
+
+ (define (allocate-cont label cont slots call-allocs)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (let-values (((slots live) (allocate-defs label vars slots)))
+ (match exp
+ (($ $call proc args)
+ (allocate-call label k (cons proc args) slots call-allocs live))
+ (($ $callk _ proc args)
+ (allocate-call label k (cons proc args) slots call-allocs live))
+ (($ $values args)
+ (allocate-values label k args slots call-allocs))
+ (($ $prompt escape? tag handler)
+ (allocate-prompt label k handler slots call-allocs))
+ (_
+ (values slots call-allocs)))))
+ (_
+ (values slots call-allocs))))
+
+ (call-with-values (lambda ()
+ (let ((slots (allocate-args cps)))
+ (intmap-fold allocate-cont cps slots empty-intmap)))
+ (lambda (slots calls)
+ (let* ((slots (allocate-lazy-vars cps slots calls live-in lazy))
+ (shuffles (compute-shuffles cps slots calls live-in))
+ (frame-sizes (compute-frame-sizes cps slots calls shuffles)))
+ (make-allocation slots constants calls shuffles frame-sizes))))))
diff --git a/module/language/cps/spec.scm b/module/language/cps/spec.scm
new file mode 100644
index 000000000..7330885ab
--- /dev/null
+++ b/module/language/cps/spec.scm
@@ -0,0 +1,37 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2015 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
+
+;;; Code:
+
+(define-module (language cps spec)
+ #:use-module (system base language)
+ #:use-module (language cps)
+ #:use-module (language cps compile-bytecode)
+ #:export (cps))
+
+(define* (write-cps exp #:optional (port (current-output-port)))
+ (write (unparse-cps exp) port))
+
+(define-language cps
+ #:title "CPS Intermediate Language"
+ #:reader (lambda (port env) (read port))
+ #:printer write-cps
+ #:parser parse-cps
+ #:compilers `((bytecode . ,compile-bytecode))
+ #:for-humans? #f
+ )
diff --git a/module/language/cps/specialize-primcalls.scm b/module/language/cps/specialize-primcalls.scm
new file mode 100644
index 000000000..c15fbdb3b
--- /dev/null
+++ b/module/language/cps/specialize-primcalls.scm
@@ -0,0 +1,59 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Some bytecode operations can encode an immediate as an operand.
+;;; This pass tranforms generic primcalls to these specialized
+;;; primcalls, if possible.
+;;;
+;;; Code:
+
+(define-module (language cps specialize-primcalls)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:export (specialize-primcalls))
+
+(define (specialize-primcalls conts)
+ (let ((constants (compute-constant-values conts)))
+ (define (immediate-u8? var)
+ (let ((val (intmap-ref constants var (lambda (_) #f))))
+ (and (exact-integer? val) (<= 0 val 255))))
+ (define (specialize-primcall name args)
+ (match (cons name args)
+ (('make-vector (? immediate-u8? n) init) 'make-vector/immediate)
+ (('vector-ref v (? immediate-u8? n)) 'vector-ref/immediate)
+ (('vector-set! v (? immediate-u8? n) x) 'vector-set!/immediate)
+ (('allocate-struct v (? immediate-u8? n)) 'allocate-struct/immediate)
+ (('struct-ref s (? immediate-u8? n)) 'struct-ref/immediate)
+ (('struct-set! s (? immediate-u8? n) x) 'struct-set!/immediate)
+ (_ #f)))
+ (intmap-map
+ (lambda (label cont)
+ (match cont
+ (($ $kargs names vars ($ $continue k src ($ $primcall name args)))
+ (let ((name* (specialize-primcall name args)))
+ (if name*
+ (build-cont
+ ($kargs names vars
+ ($continue k src ($primcall name* args))))
+ cont)))
+ (_ cont)))
+ conts)))
diff --git a/module/language/cps/split-rec.scm b/module/language/cps/split-rec.scm
new file mode 100644
index 000000000..2551ac643
--- /dev/null
+++ b/module/language/cps/split-rec.scm
@@ -0,0 +1,174 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Split functions bound in $rec expressions into strongly-connected
+;;; components. The result will be that each $rec binds a
+;;; strongly-connected component of mutually recursive functions.
+;;;
+;;; Code:
+
+(define-module (language cps split-rec)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (fold))
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (split-rec))
+
+(define (compute-free-vars conts kfun)
+ "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
+references."
+ (define (add-def var defs) (intset-add! defs var))
+ (define (add-defs vars defs)
+ (match vars
+ (() defs)
+ ((var . vars) (add-defs vars (add-def var defs)))))
+ (define (add-use var uses) (intset-add! uses var))
+ (define (add-uses vars uses)
+ (match vars
+ (() uses)
+ ((var . vars) (add-uses vars (add-use var uses)))))
+ (define (visit-nested-funs body)
+ (intset-fold
+ (lambda (label out)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $fun kfun)))
+ (intmap-union out (visit-fun kfun)))
+ (($ $kargs _ _ ($ $continue _ _
+ ($ $rec _ _ (($ $fun kfun) ...))))
+ (fold (lambda (kfun out)
+ (intmap-union out (visit-fun kfun)))
+ out kfun))
+ (_ out)))
+ body
+ empty-intmap))
+ (define (visit-fun kfun)
+ (let* ((body (compute-function-body conts kfun))
+ (free (visit-nested-funs body)))
+ (call-with-values
+ (lambda ()
+ (intset-fold
+ (lambda (label defs uses)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (values
+ (add-defs vars defs)
+ (match exp
+ ((or ($ $const) ($ $prim)) uses)
+ (($ $fun kfun)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ (($ $rec names vars (($ $fun kfun) ...))
+ (fold (lambda (kfun uses)
+ (intset-union (persistent-intset uses)
+ (intmap-ref free kfun)))
+ uses kfun))
+ (($ $values args)
+ (add-uses args uses))
+ (($ $call proc args)
+ (add-use proc (add-uses args uses)))
+ (($ $branch kt ($ $values (arg)))
+ (add-use arg uses))
+ (($ $branch kt ($ $primcall name args))
+ (add-uses args uses))
+ (($ $primcall name args)
+ (add-uses args uses))
+ (($ $prompt escape? tag handler)
+ (add-use tag uses)))))
+ (($ $kfun src meta self)
+ (values (add-def self defs) uses))
+ (_ (values defs uses))))
+ body empty-intset empty-intset))
+ (lambda (defs uses)
+ (intmap-add free kfun (intset-subtract
+ (persistent-intset uses)
+ (persistent-intset defs)))))))
+ (visit-fun kfun))
+
+(define (compute-split fns free-vars)
+ (define (get-free kfun)
+ ;; It's possible for a fun to have been skipped by
+ ;; compute-free-vars, if the fun isn't reachable. Fall back to
+ ;; empty-intset for the fun's free vars, in that case.
+ (intmap-ref free-vars kfun (lambda (_) empty-intset)))
+ (let* ((vars (intmap-keys fns))
+ (edges (intmap-map
+ (lambda (var kfun)
+ (intset-intersect (get-free kfun) vars))
+ fns)))
+ (compute-sorted-strongly-connected-components edges)))
+
+(define (intmap-acons k v map)
+ (intmap-add map k v))
+
+(define (split-rec conts)
+ (let ((free (compute-free-vars conts 0)))
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label cont out)
+ (match cont
+ (($ $kargs cont-names cont-vars
+ ($ $continue k src ($ $rec names vars (($ $fun kfuns) ...))))
+ (let ((fns (fold intmap-acons empty-intmap vars kfuns))
+ (fn-names (fold intmap-acons empty-intmap vars names)))
+ (match (compute-split fns free)
+ (()
+ ;; Remove trivial $rec.
+ (with-cps out
+ (setk label ($kargs cont-names cont-vars
+ ($continue k src ($values ()))))))
+ ((_)
+ ;; Bound functions already form a strongly-connected
+ ;; component.
+ out)
+ (components
+ ;; Multiple components. Split them into separate $rec
+ ;; expressions.
+ (define (build-body out components)
+ (match components
+ (()
+ (match (intmap-ref out k)
+ (($ $kargs names vars term)
+ (with-cps (intmap-remove out k)
+ term))))
+ ((vars . components)
+ (match (intset-fold
+ (lambda (var out)
+ (let ((name (intmap-ref fn-names var))
+ (fun (build-exp
+ ($fun (intmap-ref fns var)))))
+ (cons (list name var fun) out)))
+ vars '())
+ (((name var fun) ...)
+ (with-cps out
+ (let$ body (build-body components))
+ (letk kbody ($kargs name var ,body))
+ (build-term
+ ($continue kbody src ($rec name var fun)))))))))
+ (with-cps out
+ (let$ body (build-body components))
+ (setk label ($kargs cont-names cont-vars ,body)))))))
+ (_ out)))
+ conts
+ conts)))))
diff --git a/module/language/cps/type-fold.scm b/module/language/cps/type-fold.scm
new file mode 100644
index 000000000..2104b09ef
--- /dev/null
+++ b/module/language/cps/type-fold.scm
@@ -0,0 +1,425 @@
+;;; Abstract constant folding on CPS
+;;; Copyright (C) 2014, 2015 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 program. If not, see
+;;; .
+
+;;; Commentary:
+;;;
+;;; This pass uses the abstract interpretation provided by type analysis
+;;; to fold constant values and type predicates. It is most profitably
+;;; run after CSE, to take advantage of scalar replacement.
+;;;
+;;; Code:
+
+(define-module (language cps type-fold)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps renumber)
+ #:use-module (language cps types)
+ #:use-module (language cps with-cps)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:use-module (system base target)
+ #:export (type-fold))
+
+
+
+
+;; Branch folders.
+
+(define &scalar-types
+ (logior &exact-integer &flonum &char &unspecified &false &true &nil &null))
+
+(define *branch-folders* (make-hash-table))
+
+(define-syntax-rule (define-branch-folder name f)
+ (hashq-set! *branch-folders* 'name f))
+
+(define-syntax-rule (define-branch-folder-alias to from)
+ (hashq-set! *branch-folders* 'to (hashq-ref *branch-folders* 'from)))
+
+(define-syntax-rule (define-unary-branch-folder (name arg min max) body ...)
+ (define-branch-folder name (lambda (arg min max) body ...)))
+
+(define-syntax-rule (define-binary-branch-folder (name arg0 min0 max0
+ arg1 min1 max1)
+ body ...)
+ (define-branch-folder name (lambda (arg0 min0 max0 arg1 min1 max1) body ...)))
+
+(define-syntax-rule (define-unary-type-predicate-folder name &type)
+ (define-unary-branch-folder (name type min max)
+ (let ((type* (logand type &type)))
+ (cond
+ ((zero? type*) (values #t #f))
+ ((eqv? type type*) (values #t #t))
+ (else (values #f #f))))))
+
+;; All the cases that are in compile-bytecode.
+(define-unary-type-predicate-folder pair? &pair)
+(define-unary-type-predicate-folder null? &null)
+(define-unary-type-predicate-folder nil? &nil)
+(define-unary-type-predicate-folder symbol? &symbol)
+(define-unary-type-predicate-folder variable? &box)
+(define-unary-type-predicate-folder vector? &vector)
+(define-unary-type-predicate-folder struct? &struct)
+(define-unary-type-predicate-folder string? &string)
+(define-unary-type-predicate-folder number? &number)
+(define-unary-type-predicate-folder char? &char)
+
+(define-binary-branch-folder (eq? type0 min0 max0 type1 min1 max1)
+ (cond
+ ((or (zero? (logand type0 type1)) (< max0 min1) (< max1 min0))
+ (values #t #f))
+ ((and (eqv? type0 type1)
+ (eqv? min0 min1 max0 max1)
+ (zero? (logand type0 (1- type0)))
+ (not (zero? (logand type0 &scalar-types))))
+ (values #t #t))
+ (else
+ (values #f #f))))
+(define-branch-folder-alias eqv? eq?)
+(define-branch-folder-alias equal? eq?)
+
+(define (compare-ranges type0 min0 max0 type1 min1 max1)
+ (and (zero? (logand (logior type0 type1) (lognot &real)))
+ (cond ((< max0 min1) '<)
+ ((> min0 max1) '>)
+ ((= min0 max0 min1 max1) '=)
+ ((<= max0 min1) '<=)
+ ((>= min0 max1) '>=)
+ (else #f))))
+
+(define-binary-branch-folder (< type0 min0 max0 type1 min1 max1)
+ (case (compare-ranges type0 min0 max0 type1 min1 max1)
+ ((<) (values #t #t))
+ ((= >= >) (values #t #f))
+ (else (values #f #f))))
+
+(define-binary-branch-folder (<= type0 min0 max0 type1 min1 max1)
+ (case (compare-ranges type0 min0 max0 type1 min1 max1)
+ ((< <= =) (values #t #t))
+ ((>) (values #t #f))
+ (else (values #f #f))))
+
+(define-binary-branch-folder (= type0 min0 max0 type1 min1 max1)
+ (case (compare-ranges type0 min0 max0 type1 min1 max1)
+ ((=) (values #t #t))
+ ((< >) (values #t #f))
+ (else (values #f #f))))
+
+(define-binary-branch-folder (>= type0 min0 max0 type1 min1 max1)
+ (case (compare-ranges type0 min0 max0 type1 min1 max1)
+ ((> >= =) (values #t #t))
+ ((<) (values #t #f))
+ (else (values #f #f))))
+
+(define-binary-branch-folder (> type0 min0 max0 type1 min1 max1)
+ (case (compare-ranges type0 min0 max0 type1 min1 max1)
+ ((>) (values #t #t))
+ ((= <= <) (values #t #f))
+ (else (values #f #f))))
+
+(define-binary-branch-folder (logtest type0 min0 max0 type1 min1 max1)
+ (define (logand-min a b)
+ (if (< a b 0)
+ (min a b)
+ 0))
+ (define (logand-max a b)
+ (if (< a b 0)
+ 0
+ (max a b)))
+ (if (and (= min0 max0) (= min1 max1) (eqv? type0 type1 &exact-integer))
+ (values #t (logtest min0 min1))
+ (values #f #f)))
+
+
+
+
+;; Strength reduction.
+
+(define *primcall-reducers* (make-hash-table))
+
+(define-syntax-rule (define-primcall-reducer name f)
+ (hashq-set! *primcall-reducers* 'name f))
+
+(define-syntax-rule (define-unary-primcall-reducer (name cps k src
+ arg type min max)
+ body ...)
+ (define-primcall-reducer name
+ (lambda (cps k src arg type min max)
+ body ...)))
+
+(define-syntax-rule (define-binary-primcall-reducer (name cps k src
+ arg0 type0 min0 max0
+ arg1 type1 min1 max1)
+ body ...)
+ (define-primcall-reducer name
+ (lambda (cps k src arg0 type0 min0 max0 arg1 type1 min1 max1)
+ body ...)))
+
+(define-binary-primcall-reducer (mul cps k src
+ arg0 type0 min0 max0
+ arg1 type1 min1 max1)
+ (define (fail) (with-cps cps #f))
+ (define (negate arg)
+ (with-cps cps
+ ($ (with-cps-constants ((zero 0))
+ (build-term
+ ($continue k src ($primcall 'sub (zero arg))))))))
+ (define (zero)
+ (with-cps cps
+ (build-term ($continue k src ($const 0)))))
+ (define (identity arg)
+ (with-cps cps
+ (build-term ($continue k src ($values (arg))))))
+ (define (double arg)
+ (with-cps cps
+ (build-term ($continue k src ($primcall 'add (arg arg))))))
+ (define (power-of-two constant arg)
+ (let ((n (let lp ((bits 0) (constant constant))
+ (if (= constant 1) bits (lp (1+ bits) (ash constant -1))))))
+ (with-cps cps
+ ($ (with-cps-constants ((bits n))
+ (build-term ($continue k src ($primcall 'ash (arg bits)))))))))
+ (define (mul/constant constant constant-type arg arg-type)
+ (cond
+ ((not (or (= constant-type &exact-integer) (= constant-type arg-type)))
+ (fail))
+ ((eqv? constant -1)
+ ;; (* arg -1) -> (- 0 arg)
+ (negate arg))
+ ((eqv? constant 0)
+ ;; (* arg 0) -> 0 if arg is not a flonum or complex
+ (and (= constant-type &exact-integer)
+ (zero? (logand arg-type
+ (lognot (logior &flonum &complex))))
+ (zero)))
+ ((eqv? constant 1)
+ ;; (* arg 1) -> arg
+ (identity arg))
+ ((eqv? constant 2)
+ ;; (* arg 2) -> (+ arg arg)
+ (double arg))
+ ((and (= constant-type arg-type &exact-integer)
+ (positive? constant)
+ (zero? (logand constant (1- constant))))
+ ;; (* arg power-of-2) -> (ash arg (log2 power-of-2
+ (power-of-two constant arg))
+ (else
+ (fail))))
+ (cond
+ ((logtest (logior type0 type1) (lognot &number)) (fail))
+ ((= min0 max0) (mul/constant min0 type0 arg1 type1))
+ ((= min1 max1) (mul/constant min1 type1 arg0 type0))
+ (else (fail))))
+
+(define-binary-primcall-reducer (logbit? cps k src
+ arg0 type0 min0 max0
+ arg1 type1 min1 max1)
+ (define (convert-to-logtest cps kbool)
+ (define (compute-mask cps kmask src)
+ (if (eq? min0 max0)
+ (with-cps cps
+ (build-term
+ ($continue kmask src ($const (ash 1 min0)))))
+ (with-cps cps
+ ($ (with-cps-constants ((one 1))
+ (build-term
+ ($continue kmask src ($primcall 'ash (one arg0)))))))))
+ (with-cps cps
+ (letv mask)
+ (letk kt ($kargs () ()
+ ($continue kbool src ($const #t))))
+ (letk kf ($kargs () ()
+ ($continue kbool src ($const #f))))
+ (letk kmask ($kargs (#f) (mask)
+ ($continue kf src
+ ($branch kt ($primcall 'logtest (mask arg1))))))
+ ($ (compute-mask kmask src))))
+ ;; Hairiness because we are converting from a primcall with unknown
+ ;; arity to a branching primcall.
+ (let ((positive-fixnum-bits (- (* (target-word-size) 8) 3)))
+ (if (and (= type0 &exact-integer)
+ (<= 0 min0 positive-fixnum-bits)
+ (<= 0 max0 positive-fixnum-bits))
+ (match (intmap-ref cps k)
+ (($ $kreceive arity kargs)
+ (match arity
+ (($ $arity (_) () (not #f) () #f)
+ (with-cps cps
+ (letv bool)
+ (let$ body (with-cps-constants ((nil '()))
+ (build-term
+ ($continue kargs src ($values (bool nil))))))
+ (letk kbool ($kargs (#f) (bool) ,body))
+ ($ (convert-to-logtest kbool))))
+ (_
+ (with-cps cps
+ (letv bool)
+ (letk kbool ($kargs (#f) (bool)
+ ($continue k src ($primcall 'values (bool)))))
+ ($ (convert-to-logtest kbool))))))
+ (($ $ktail)
+ (with-cps cps
+ (letv bool)
+ (letk kbool ($kargs (#f) (bool)
+ ($continue k src ($primcall 'return (bool)))))
+ ($ (convert-to-logtest kbool)))))
+ (with-cps cps #f))))
+
+
+
+
+;;
+
+(define (local-type-fold start end cps)
+ (define (scalar-value type val)
+ (cond
+ ((eqv? type &exact-integer) val)
+ ((eqv? type &flonum) (exact->inexact val))
+ ((eqv? type &char) (integer->char val))
+ ((eqv? type &unspecified) *unspecified*)
+ ((eqv? type &false) #f)
+ ((eqv? type &true) #t)
+ ((eqv? type &nil) #nil)
+ ((eqv? type &null) '())
+ (else (error "unhandled type" type val))))
+ (let ((types (infer-types cps start)))
+ (define (fold-primcall cps label names vars k src name args def)
+ (call-with-values (lambda () (lookup-post-type types label def 0))
+ (lambda (type min max)
+ (and (not (zero? type))
+ (zero? (logand type (1- type)))
+ (zero? (logand type (lognot &scalar-types)))
+ (eqv? min max)
+ (let ((val (scalar-value type min)))
+ ;; (pk 'folded src name args val)
+ (with-cps cps
+ (letv v*)
+ (letk k* ($kargs (#f) (v*)
+ ($continue k src ($const val))))
+ ;; Rely on DCE to elide this expression, if
+ ;; possible.
+ (setk label
+ ($kargs names vars
+ ($continue k* src ($primcall name args))))))))))
+ (define (reduce-primcall cps label names vars k src name args)
+ (and=>
+ (hashq-ref *primcall-reducers* name)
+ (lambda (reducer)
+ (match args
+ ((arg0)
+ (call-with-values (lambda () (lookup-pre-type types label arg0))
+ (lambda (type0 min0 max0)
+ (call-with-values (lambda ()
+ (reducer cps k src arg0 type0 min0 max0))
+ (lambda (cps term)
+ (and term
+ (with-cps cps
+ (setk label ($kargs names vars ,term)))))))))
+ ((arg0 arg1)
+ (call-with-values (lambda () (lookup-pre-type types label arg0))
+ (lambda (type0 min0 max0)
+ (call-with-values (lambda () (lookup-pre-type types label arg1))
+ (lambda (type1 min1 max1)
+ (call-with-values (lambda ()
+ (reducer cps k src arg0 type0 min0 max0
+ arg1 type1 min1 max1))
+ (lambda (cps term)
+ (and term
+ (with-cps cps
+ (setk label ($kargs names vars ,term)))))))))))
+ (_ #f)))))
+ (define (fold-unary-branch cps label names vars kf kt src name arg)
+ (and=>
+ (hashq-ref *branch-folders* name)
+ (lambda (folder)
+ (call-with-values (lambda () (lookup-pre-type types label arg))
+ (lambda (type min max)
+ (call-with-values (lambda () (folder type min max))
+ (lambda (f? v)
+ ;; (when f? (pk 'folded-unary-branch label name arg v))
+ (and f?
+ (with-cps cps
+ (setk label
+ ($kargs names vars
+ ($continue (if v kt kf) src
+ ($values ())))))))))))))
+ (define (fold-binary-branch cps label names vars kf kt src name arg0 arg1)
+ (and=>
+ (hashq-ref *branch-folders* name)
+ (lambda (folder)
+ (call-with-values (lambda () (lookup-pre-type types label arg0))
+ (lambda (type0 min0 max0)
+ (call-with-values (lambda () (lookup-pre-type types label arg1))
+ (lambda (type1 min1 max1)
+ (call-with-values (lambda ()
+ (folder type0 min0 max0 type1 min1 max1))
+ (lambda (f? v)
+ ;; (when f? (pk 'folded-binary-branch label name arg0 arg1 v))
+ (and f?
+ (with-cps cps
+ (setk label
+ ($kargs names vars
+ ($continue (if v kt kf) src
+ ($values ())))))))))))))))
+ (define (visit-expression cps label names vars k src exp)
+ (match exp
+ (($ $primcall name args)
+ ;; We might be able to fold primcalls that define a value.
+ (match (intmap-ref cps k)
+ (($ $kargs (_) (def))
+ (or (fold-primcall cps label names vars k src name args def)
+ (reduce-primcall cps label names vars k src name args)
+ cps))
+ (_
+ (or (reduce-primcall cps label names vars k src name args)
+ cps))))
+ (($ $branch kt ($ $primcall name args))
+ ;; We might be able to fold primcalls that branch.
+ (match args
+ ((x)
+ (or (fold-unary-branch cps label names vars k kt src name x)
+ cps))
+ ((x y)
+ (or (fold-binary-branch cps label names vars k kt src name x y)
+ cps))))
+ (_ cps)))
+ (let lp ((label start) (cps cps))
+ (if (<= label end)
+ (lp (1+ label)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-expression cps label names vars k src exp))
+ (_ cps)))
+ cps))))
+
+(define (fold-functions-in-renumbered-program f conts seed)
+ (let* ((conts (persistent-intmap conts))
+ (end (1+ (intmap-prev conts))))
+ (let lp ((label 0) (seed seed))
+ (if (eqv? label end)
+ seed
+ (match (intmap-ref conts label)
+ (($ $kfun src meta self tail clause)
+ (lp (1+ tail) (f label tail seed))))))))
+
+(define (type-fold conts)
+ ;; Type analysis wants a program whose labels are sorted.
+ (let ((conts (renumber conts)))
+ (with-fresh-name-state conts
+ (persistent-intmap
+ (fold-functions-in-renumbered-program local-type-fold conts conts)))))
diff --git a/module/language/cps/types.scm b/module/language/cps/types.scm
new file mode 100644
index 000000000..55cde2744
--- /dev/null
+++ b/module/language/cps/types.scm
@@ -0,0 +1,1408 @@
+;;; Type analysis on CPS
+;;; Copyright (C) 2014, 2015 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 program. If not, see
+;;; .
+
+;;; Commentary:
+;;;
+;;; Type analysis computes the possible types and ranges that values may
+;;; have at all program positions. This analysis can help to prove that
+;;; a primcall has no side-effects, if its arguments have the
+;;; appropriate type and range. It can also enable constant folding of
+;;; type predicates and, in the future, enable the compiler to choose
+;;; untagged, unboxed representations for numbers.
+;;;
+;;; For the purposes of this analysis, a "type" is an aspect of a value
+;;; that will not change. Guile's CPS intermediate language does not
+;;; carry manifest type information that asserts properties about given
+;;; values; instead, we recover this information via flow analysis,
+;;; garnering properties from type predicates, constant literals,
+;;; primcall results, and primcalls that assert that their arguments are
+;;; of particular types.
+;;;
+;;; A range denotes a subset of the set of values in a type, bounded by
+;;; a minimum and a maximum. The precise meaning of a range depends on
+;;; the type. For real numbers, the range indicates an inclusive lower
+;;; and upper bound on the integer value of a type. For vectors, the
+;;; range indicates the length of the vector. The range is limited to a
+;;; signed 32-bit value, with the smallest and largest values indicating
+;;; -inf.0 and +inf.0, respectively. For some types, like pairs, the
+;;; concept of "range" makes no sense. In these cases we consider the
+;;; range to be -inf.0 to +inf.0.
+;;;
+;;; Types are represented as a bitfield. Fewer bits means a more precise
+;;; type. Although normally only values that have a single type will
+;;; have an associated range, this is not enforced. The range applies
+;;; to all types in the bitfield. When control flow meets, the types and
+;;; ranges meet with the union operator.
+;;;
+;;; It is not practical to precisely compute value ranges in all cases.
+;;; For example, in the following case:
+;;;
+;;; (let lp ((n 0)) (when (foo) (lp (1+ n))))
+;;;
+;;; The first time that range analysis visits the program, N is
+;;; determined to be the exact integer 0. The second time, it is an
+;;; exact integer in the range [0, 1]; the third, [0, 2]; and so on.
+;;; This analysis will terminate, but only after the positive half of
+;;; the 32-bit range has been fully explored and we decide that the
+;;; range of N is [0, +inf.0]. At the same time, we want to do range
+;;; analysis and type analysis at the same time, as there are
+;;; interactions between them, notably in the case of `sqrt' which
+;;; returns a complex number if its argument cannot be proven to be
+;;; non-negative. So what we do instead is to precisely propagate types
+;;; and ranges when propagating forward, but after the first backwards
+;;; branch is seen, we cause backward branches that would expand the
+;;; range of a value to saturate that range towards positive or negative
+;;; infinity (as appropriate).
+;;;
+;;; A naive approach to type analysis would build up a table that has
+;;; entries for all variables at all program points, but this has
+;;; N-squared complexity and quickly grows unmanageable. Instead, we
+;;; use _intmaps_ from (language cps intmap) to share state between
+;;; connected program points.
+;;;
+;;; Code:
+
+(define-module (language cps types)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:use-module (rnrs bytevectors)
+ #:use-module (srfi srfi-11)
+ #:export (;; Specific types.
+ &exact-integer
+ &flonum
+ &complex
+ &fraction
+
+ &char
+ &unspecified
+ &unbound
+ &false
+ &true
+ &nil
+ &null
+ &symbol
+ &keyword
+
+ &procedure
+
+ &pointer
+ &fluid
+ &pair
+ &vector
+ &box
+ &struct
+ &string
+ &bytevector
+ &bitvector
+ &array
+ &hash-table
+
+ ;; Union types.
+ &number &real
+
+ infer-types
+ lookup-pre-type
+ lookup-post-type
+ primcall-types-check?))
+
+(define-syntax define-flags
+ (lambda (x)
+ (syntax-case x ()
+ ((_ all shift name ...)
+ (let ((count (length #'(name ...))))
+ (with-syntax (((n ...) (iota count))
+ (count count))
+ #'(begin
+ (define-syntax name (identifier-syntax (ash 1 n)))
+ ...
+ (define-syntax all (identifier-syntax (1- (ash 1 count))))
+ (define-syntax shift (identifier-syntax count)))))))))
+
+;; More precise types have fewer bits.
+(define-flags &all-types &type-bits
+ &exact-integer
+ &flonum
+ &complex
+ &fraction
+
+ &char
+ &unspecified
+ &unbound
+ &false
+ &true
+ &nil
+ &null
+ &symbol
+ &keyword
+
+ &procedure
+
+ &pointer
+ &fluid
+ &pair
+ &vector
+ &box
+ &struct
+ &string
+ &bytevector
+ &bitvector
+ &array
+ &hash-table)
+
+(define-syntax &no-type (identifier-syntax 0))
+
+(define-syntax &number
+ (identifier-syntax (logior &exact-integer &flonum &complex &fraction)))
+(define-syntax &real
+ (identifier-syntax (logior &exact-integer &flonum &fraction)))
+
+(define-syntax *max-s32* (identifier-syntax (- (ash 1 31) 1)))
+(define-syntax *min-s32* (identifier-syntax (- 0 (ash 1 31))))
+
+;; Versions of min and max that do not coerce exact numbers to become
+;; inexact.
+(define min
+ (case-lambda
+ ((a b) (if (< a b) a b))
+ ((a b c) (min (min a b) c))
+ ((a b c d) (min (min a b) c d))))
+(define max
+ (case-lambda
+ ((a b) (if (> a b) a b))
+ ((a b c) (max (max a b) c))
+ ((a b c d) (max (max a b) c d))))
+
+
+
+(define-syntax-rule (define-compile-time-value name val)
+ (define-syntax name
+ (make-variable-transformer
+ (lambda (x)
+ (syntax-case x (set!)
+ (var (identifier? #'var)
+ (datum->syntax #'var val)))))))
+
+(define-compile-time-value min-fixnum most-negative-fixnum)
+(define-compile-time-value max-fixnum most-positive-fixnum)
+
+(define-inlinable (make-unclamped-type-entry type min max)
+ (vector type min max))
+(define-inlinable (type-entry-type tentry)
+ (vector-ref tentry 0))
+(define-inlinable (type-entry-clamped-min tentry)
+ (vector-ref tentry 1))
+(define-inlinable (type-entry-clamped-max tentry)
+ (vector-ref tentry 2))
+
+(define-syntax-rule (clamp-range val)
+ (cond
+ ((< val min-fixnum) min-fixnum)
+ ((< max-fixnum val) max-fixnum)
+ (else val)))
+
+(define-inlinable (make-type-entry type min max)
+ (vector type (clamp-range min) (clamp-range max)))
+(define-inlinable (type-entry-min tentry)
+ (let ((min (type-entry-clamped-min tentry)))
+ (if (eq? min min-fixnum) -inf.0 min)))
+(define-inlinable (type-entry-max tentry)
+ (let ((max (type-entry-clamped-max tentry)))
+ (if (eq? max max-fixnum) +inf.0 max)))
+
+(define all-types-entry (make-type-entry &all-types -inf.0 +inf.0))
+
+(define* (var-type-entry typeset var #:optional (default all-types-entry))
+ (intmap-ref typeset var (lambda (_) default)))
+
+(define (var-type typeset var)
+ (type-entry-type (var-type-entry typeset var)))
+(define (var-min typeset var)
+ (type-entry-min (var-type-entry typeset var)))
+(define (var-max typeset var)
+ (type-entry-max (var-type-entry typeset var)))
+
+;; Is the type entry A contained entirely within B?
+(define (type-entry<=? a b)
+ (match (cons a b)
+ ((#(a-type a-min a-max) . #(b-type b-min b-max))
+ (and (eqv? b-type (logior a-type b-type))
+ (<= b-min a-min)
+ (>= b-max a-max)))))
+
+(define (type-entry-union a b)
+ (cond
+ ((type-entry<=? b a) a)
+ ((type-entry<=? a b) b)
+ (else (make-type-entry
+ (logior (type-entry-type a) (type-entry-type b))
+ (min (type-entry-clamped-min a) (type-entry-clamped-min b))
+ (max (type-entry-clamped-max a) (type-entry-clamped-max b))))))
+
+(define (type-entry-saturating-union a b)
+ (cond
+ ((type-entry<=? b a) a)
+ (else
+ (make-type-entry
+ (logior (type-entry-type a) (type-entry-type b))
+ (let ((a-min (type-entry-clamped-min a))
+ (b-min (type-entry-clamped-min b)))
+ (if (< b-min a-min) min-fixnum a-min))
+ (let ((a-max (type-entry-clamped-max a))
+ (b-max (type-entry-clamped-max b)))
+ (if (> b-max a-max) max-fixnum a-max))))))
+
+(define (type-entry-intersection a b)
+ (cond
+ ((type-entry<=? a b) a)
+ ((type-entry<=? b a) b)
+ (else (make-type-entry
+ (logand (type-entry-type a) (type-entry-type b))
+ (max (type-entry-clamped-min a) (type-entry-clamped-min b))
+ (min (type-entry-clamped-max a) (type-entry-clamped-max b))))))
+
+(define (adjoin-var typeset var entry)
+ (intmap-add typeset var entry type-entry-union))
+
+(define (restrict-var typeset var entry)
+ (intmap-add typeset var entry type-entry-intersection))
+
+(define (constant-type val)
+ "Compute the type and range of VAL. Return three values: the type,
+minimum, and maximum."
+ (define (return type val)
+ (if val
+ (make-type-entry type val val)
+ (make-type-entry type -inf.0 +inf.0)))
+ (cond
+ ((number? val)
+ (cond
+ ((exact-integer? val) (return &exact-integer val))
+ ((eqv? (imag-part val) 0)
+ (if (nan? val)
+ (make-type-entry &flonum -inf.0 +inf.0)
+ (make-type-entry
+ (if (exact? val) &fraction &flonum)
+ (if (rational? val) (inexact->exact (floor val)) val)
+ (if (rational? val) (inexact->exact (ceiling val)) val))))
+ (else (return &complex #f))))
+ ((eq? val '()) (return &null #f))
+ ((eq? val #nil) (return &nil #f))
+ ((eq? val #t) (return &true #f))
+ ((eq? val #f) (return &false #f))
+ ((char? val) (return &char (char->integer val)))
+ ((eqv? val *unspecified*) (return &unspecified #f))
+ ((symbol? val) (return &symbol #f))
+ ((keyword? val) (return &keyword #f))
+ ((pair? val) (return &pair #f))
+ ((vector? val) (return &vector (vector-length val)))
+ ((string? val) (return &string (string-length val)))
+ ((bytevector? val) (return &bytevector (bytevector-length val)))
+ ((bitvector? val) (return &bitvector (bitvector-length val)))
+ ((array? val) (return &array (array-rank val)))
+ ((not (variable-bound? (make-variable val))) (return &unbound #f))
+
+ (else (error "unhandled constant" val))))
+
+(define *type-checkers* (make-hash-table))
+(define *type-inferrers* (make-hash-table))
+
+(define-syntax-rule (define-type-helper name)
+ (define-syntax-parameter name
+ (lambda (stx)
+ (syntax-violation 'name
+ "macro used outside of define-type"
+ stx))))
+(define-type-helper define!)
+(define-type-helper restrict!)
+(define-type-helper &type)
+(define-type-helper &min)
+(define-type-helper &max)
+
+(define-syntax-rule (define-type-checker (name arg ...) body ...)
+ (hashq-set!
+ *type-checkers*
+ 'name
+ (lambda (typeset arg ...)
+ (syntax-parameterize
+ ((&type (syntax-rules () ((_ val) (var-type typeset val))))
+ (&min (syntax-rules () ((_ val) (var-min typeset val))))
+ (&max (syntax-rules () ((_ val) (var-max typeset val)))))
+ body ...))))
+
+(define-syntax-rule (check-type arg type min max)
+ ;; If the arg is negative, it is a closure variable.
+ (and (>= arg 0)
+ (zero? (logand (lognot type) (&type arg)))
+ (<= min (&min arg))
+ (<= (&max arg) max)))
+
+(define-syntax-rule (define-type-inferrer* (name succ var ...) body ...)
+ (hashq-set!
+ *type-inferrers*
+ 'name
+ (lambda (in succ var ...)
+ (let ((out in))
+ (syntax-parameterize
+ ((define!
+ (syntax-rules ()
+ ((_ val type min max)
+ (set! out (adjoin-var out val
+ (make-type-entry type min max))))))
+ (restrict!
+ (syntax-rules ()
+ ((_ val type min max)
+ (set! out (restrict-var out val
+ (make-type-entry type min max))))))
+ (&type (syntax-rules () ((_ val) (var-type in val))))
+ (&min (syntax-rules () ((_ val) (var-min in val))))
+ (&max (syntax-rules () ((_ val) (var-max in val)))))
+ body ...
+ out)))))
+
+(define-syntax-rule (define-type-inferrer (name arg ...) body ...)
+ (define-type-inferrer* (name succ arg ...) body ...))
+
+(define-syntax-rule (define-predicate-inferrer (name arg ... true?) body ...)
+ (define-type-inferrer* (name succ arg ...)
+ (let ((true? (not (zero? succ))))
+ body ...)))
+
+(define-syntax define-simple-type-checker
+ (lambda (x)
+ (define (parse-spec l)
+ (syntax-case l ()
+ (() '())
+ (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
+ (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
+ ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
+ (syntax-case x ()
+ ((_ (name arg-spec ...) result-spec ...)
+ (with-syntax
+ (((arg ...) (generate-temporaries #'(arg-spec ...)))
+ (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...))))
+ #'(define-type-checker (name arg ...)
+ (and (check-type arg arg-type arg-min arg-max)
+ ...)))))))
+
+(define-syntax define-simple-type-inferrer
+ (lambda (x)
+ (define (parse-spec l)
+ (syntax-case l ()
+ (() '())
+ (((type min max) . l) (cons #'(type min max) (parse-spec #'l)))
+ (((type min+max) . l) (cons #'(type min+max min+max) (parse-spec #'l)))
+ ((type . l) (cons #'(type -inf.0 +inf.0) (parse-spec #'l)))))
+ (syntax-case x ()
+ ((_ (name arg-spec ...) result-spec ...)
+ (with-syntax
+ (((arg ...) (generate-temporaries #'(arg-spec ...)))
+ (((arg-type arg-min arg-max) ...) (parse-spec #'(arg-spec ...)))
+ ((res ...) (generate-temporaries #'(result-spec ...)))
+ (((res-type res-min res-max) ...) (parse-spec #'(result-spec ...))))
+ #'(define-type-inferrer (name arg ... res ...)
+ (restrict! arg arg-type arg-min arg-max)
+ ...
+ (define! res res-type res-min res-max)
+ ...))))))
+
+(define-syntax-rule (define-simple-type (name arg-spec ...) result-spec ...)
+ (begin
+ (define-simple-type-checker (name arg-spec ...))
+ (define-simple-type-inferrer (name arg-spec ...) result-spec ...)))
+
+(define-syntax-rule (define-simple-types
+ ((name arg-spec ...) result-spec ...)
+ ...)
+ (begin
+ (define-simple-type (name arg-spec ...) result-spec ...)
+ ...))
+
+(define-syntax-rule (define-type-checker-aliases orig alias ...)
+ (let ((check (hashq-ref *type-checkers* 'orig)))
+ (hashq-set! *type-checkers* 'alias check)
+ ...))
+(define-syntax-rule (define-type-inferrer-aliases orig alias ...)
+ (let ((check (hashq-ref *type-inferrers* 'orig)))
+ (hashq-set! *type-inferrers* 'alias check)
+ ...))
+(define-syntax-rule (define-type-aliases orig alias ...)
+ (begin
+ (define-type-checker-aliases orig alias ...)
+ (define-type-inferrer-aliases orig alias ...)))
+
+
+
+
+;;; This list of primcall type definitions follows the order of
+;;; effects-analysis.scm; please keep it in a similar order.
+;;;
+;;; There is no need to add checker definitions for expressions that do
+;;; not exhibit the &type-check effect, as callers should not ask if
+;;; such an expression does or does not type-check. For those that do
+;;; exhibit &type-check, you should define a type inferrer unless the
+;;; primcall will never typecheck.
+;;;
+;;; Likewise there is no need to define inferrers for primcalls which
+;;; return &all-types values and which never raise exceptions from which
+;;; we can infer the types of incoming values.
+
+
+
+
+;;;
+;;; Generic effect-free predicates.
+;;;
+
+(define-predicate-inferrer (eq? a b true?)
+ ;; We can only propagate information down the true leg.
+ (when true?
+ (let ((type (logand (&type a) (&type b)))
+ (min (max (&min a) (&min b)))
+ (max (min (&max a) (&max b))))
+ (restrict! a type min max)
+ (restrict! b type min max))))
+(define-type-inferrer-aliases eq? eqv? equal?)
+
+(define-syntax-rule (define-simple-predicate-inferrer predicate type)
+ (define-predicate-inferrer (predicate val true?)
+ (let ((type (if true?
+ type
+ (logand (&type val) (lognot type)))))
+ (restrict! val type -inf.0 +inf.0))))
+(define-simple-predicate-inferrer pair? &pair)
+(define-simple-predicate-inferrer null? &null)
+(define-simple-predicate-inferrer nil? &nil)
+(define-simple-predicate-inferrer symbol? &symbol)
+(define-simple-predicate-inferrer variable? &box)
+(define-simple-predicate-inferrer vector? &vector)
+(define-simple-predicate-inferrer struct? &struct)
+(define-simple-predicate-inferrer string? &string)
+(define-simple-predicate-inferrer bytevector? &bytevector)
+(define-simple-predicate-inferrer bitvector? &bitvector)
+(define-simple-predicate-inferrer keyword? &keyword)
+(define-simple-predicate-inferrer number? &number)
+(define-simple-predicate-inferrer char? &char)
+(define-simple-predicate-inferrer procedure? &procedure)
+(define-simple-predicate-inferrer thunk? &procedure)
+
+
+
+;;;
+;;; Fluids. Note that we can't track bound-ness of fluids, as pop-fluid
+;;; can change boundness.
+;;;
+
+(define-simple-types
+ ((fluid-ref (&fluid 1)) &all-types)
+ ((fluid-set! (&fluid 0 1) &all-types))
+ ((push-fluid (&fluid 0 1) &all-types))
+ ((pop-fluid)))
+
+
+
+
+;;;
+;;; Prompts. (Nothing to do.)
+;;;
+
+
+
+
+;;;
+;;; Pairs.
+;;;
+
+(define-simple-types
+ ((cons &all-types &all-types) &pair)
+ ((car &pair) &all-types)
+ ((set-car! &pair &all-types))
+ ((cdr &pair) &all-types)
+ ((set-cdr! &pair &all-types)))
+
+
+
+
+;;;
+;;; Variables.
+;;;
+
+(define-simple-types
+ ((box &all-types) (&box 1))
+ ((box-ref (&box 1)) &all-types))
+
+(define-simple-type-checker (box-set! (&box 0 1) &all-types))
+(define-type-inferrer (box-set! box val)
+ (restrict! box &box 1 1))
+
+
+
+
+;;;
+;;; Vectors.
+;;;
+
+;; This max-vector-len computation is a hack.
+(define *max-vector-len* (ash most-positive-fixnum -5))
+
+(define-simple-type-checker (make-vector (&exact-integer 0 *max-vector-len*)
+ &all-types))
+(define-type-inferrer (make-vector size init result)
+ (restrict! size &exact-integer 0 *max-vector-len*)
+ (define! result &vector (max (&min size) 0) (&max size)))
+
+(define-type-checker (vector-ref v idx)
+ (and (check-type v &vector 0 *max-vector-len*)
+ (check-type idx &exact-integer 0 (1- (&min v)))))
+(define-type-inferrer (vector-ref v idx result)
+ (restrict! v &vector (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max v)))
+ (define! result &all-types -inf.0 +inf.0))
+
+(define-type-checker (vector-set! v idx val)
+ (and (check-type v &vector 0 *max-vector-len*)
+ (check-type idx &exact-integer 0 (1- (&min v)))))
+(define-type-inferrer (vector-set! v idx val)
+ (restrict! v &vector (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max v))))
+
+(define-type-aliases make-vector make-vector/immediate)
+(define-type-aliases vector-ref vector-ref/immediate)
+(define-type-aliases vector-set! vector-set!/immediate)
+
+(define-simple-type-checker (vector-length &vector))
+(define-type-inferrer (vector-length v result)
+ (restrict! v &vector 0 *max-vector-len*)
+ (define! result &exact-integer (max (&min v) 0)
+ (min (&max v) *max-vector-len*)))
+
+
+
+
+;;;
+;;; Structs.
+;;;
+
+;; No type-checker for allocate-struct, as we can't currently check that
+;; vt is actually a vtable.
+(define-type-inferrer (allocate-struct vt size result)
+ (restrict! vt &struct vtable-offset-user +inf.0)
+ (restrict! size &exact-integer 0 +inf.0)
+ (define! result &struct (max (&min size) 0) (&max size)))
+
+(define-type-checker (struct-ref s idx)
+ (and (check-type s &struct 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ ;; FIXME: is the field readable?
+ (< (&max idx) (&min s))))
+(define-type-inferrer (struct-ref s idx result)
+ (restrict! s &struct (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max s)))
+ (define! result &all-types -inf.0 +inf.0))
+
+(define-type-checker (struct-set! s idx val)
+ (and (check-type s &struct 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ ;; FIXME: is the field writable?
+ (< (&max idx) (&min s))))
+(define-type-inferrer (struct-set! s idx val)
+ (restrict! s &struct (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max s))))
+
+(define-type-aliases allocate-struct allocate-struct/immediate)
+(define-type-aliases struct-ref struct-ref/immediate)
+(define-type-aliases struct-set! struct-set!/immediate)
+
+(define-simple-type (struct-vtable (&struct 0 +inf.0))
+ (&struct vtable-offset-user +inf.0))
+
+
+
+
+;;;
+;;; Strings.
+;;;
+
+(define *max-char* (1- (ash 1 24)))
+
+(define-type-checker (string-ref s idx)
+ (and (check-type s &string 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ (< (&max idx) (&min s))))
+(define-type-inferrer (string-ref s idx result)
+ (restrict! s &string (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max s)))
+ (define! result &char 0 *max-char*))
+
+(define-type-checker (string-set! s idx val)
+ (and (check-type s &string 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ (check-type val &char 0 *max-char*)
+ (< (&max idx) (&min s))))
+(define-type-inferrer (string-set! s idx val)
+ (restrict! s &string (1+ (&min idx)) +inf.0)
+ (restrict! idx &exact-integer 0 (1- (&max s)))
+ (restrict! val &char 0 *max-char*))
+
+(define-simple-type-checker (string-length &string))
+(define-type-inferrer (string-length s result)
+ (restrict! s &string 0 +inf.0)
+ (define! result &exact-integer (max (&min s) 0) (&max s)))
+
+(define-simple-type (number->string &number) (&string 0 +inf.0))
+(define-simple-type (string->number (&string 0 +inf.0))
+ ((logior &number &false) -inf.0 +inf.0))
+
+
+
+
+;;;
+;;; Bytevectors.
+;;;
+
+(define-simple-type-checker (bytevector-length &bytevector))
+(define-type-inferrer (bytevector-length bv result)
+ (restrict! bv &bytevector 0 +inf.0)
+ (define! result &exact-integer (max (&min bv) 0) (&max bv)))
+
+(define-syntax-rule (define-bytevector-accessors ref set type size min max)
+ (begin
+ (define-type-checker (ref bv idx)
+ (and (check-type bv &bytevector 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ (< (&max idx) (- (&min bv) size))))
+ (define-type-inferrer (ref bv idx result)
+ (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
+ (restrict! idx &exact-integer 0 (- (&max bv) size))
+ (define! result type min max))
+ (define-type-checker (set bv idx val)
+ (and (check-type bv &bytevector 0 +inf.0)
+ (check-type idx &exact-integer 0 +inf.0)
+ (check-type val type min max)
+ (< (&max idx) (- (&min bv) size))))
+ (define-type-inferrer (set! bv idx val)
+ (restrict! bv &bytevector (+ (&min idx) size) +inf.0)
+ (restrict! idx &exact-integer 0 (- (&max bv) size))
+ (restrict! val type min max))))
+
+(define-syntax-rule (define-short-bytevector-accessors ref set size signed?)
+ (define-bytevector-accessors ref set &exact-integer size
+ (if signed? (- (ash 1 (1- (* size 8)))) 0)
+ (1- (ash 1 (if signed? (1- (* size 8)) (* size 8))))))
+
+(define-short-bytevector-accessors bv-u8-ref bv-u8-set! 1 #f)
+(define-short-bytevector-accessors bv-s8-ref bv-s8-set! 1 #t)
+(define-short-bytevector-accessors bv-u16-ref bv-u16-set! 2 #f)
+(define-short-bytevector-accessors bv-s16-ref bv-s16-set! 2 #t)
+
+;; The range analysis only works on signed 32-bit values, so some limits
+;; are out of range.
+(define-bytevector-accessors bv-u32-ref bv-u32-set! &exact-integer 4 0 +inf.0)
+(define-bytevector-accessors bv-s32-ref bv-s32-set! &exact-integer 4 -inf.0 +inf.0)
+(define-bytevector-accessors bv-u64-ref bv-u64-set! &exact-integer 8 0 +inf.0)
+(define-bytevector-accessors bv-s64-ref bv-s64-set! &exact-integer 8 -inf.0 +inf.0)
+(define-bytevector-accessors bv-f32-ref bv-f32-set! &real 4 -inf.0 +inf.0)
+(define-bytevector-accessors bv-f64-ref bv-f64-set! &real 8 -inf.0 +inf.0)
+
+
+
+
+;;;
+;;; Numbers.
+;;;
+
+;; First, branching primitives with no results.
+(define-simple-type-checker (= &number &number))
+(define-predicate-inferrer (= a b true?)
+ (when (and true?
+ (zero? (logand (logior (&type a) (&type b)) (lognot &number))))
+ (let ((min (max (&min a) (&min b)))
+ (max (min (&max a) (&max b))))
+ (restrict! a &number min max)
+ (restrict! b &number min max))))
+
+(define (restricted-comparison-ranges op type0 min0 max0 type1 min1 max1)
+ (define (infer-integer-ranges)
+ (match op
+ ('< (values min0 (min max0 (1- max1)) (max (1+ min0) min1) max1))
+ ('<= (values min0 (min max0 max1) (max min0 min1) max1))
+ ('>= (values (max min0 min1) max0 min1 (min max0 max1)))
+ ('> (values (max min0 (1+ min1)) max0 min1 (min (1- max0) max1)))))
+ (define (infer-real-ranges)
+ (match op
+ ((or '< '<=) (values min0 (min max0 max1) (max min0 min1) max1))
+ ((or '> '>=) (values (max min0 min1) max0 min1 (min max0 max1)))))
+ (if (= (logior type0 type1) &exact-integer)
+ (infer-integer-ranges)
+ (infer-real-ranges)))
+
+(define-syntax-rule (define-comparison-inferrer (op inverse))
+ (define-predicate-inferrer (op a b true?)
+ (when (zero? (logand (logior (&type a) (&type b)) (lognot &number)))
+ (call-with-values
+ (lambda ()
+ (restricted-comparison-ranges (if true? 'op 'inverse)
+ (&type a) (&min a) (&max a)
+ (&type b) (&min b) (&max b)))
+ (lambda (min0 max0 min1 max1)
+ (restrict! a &real min0 max0)
+ (restrict! b &real min1 max1))))))
+
+(define-simple-type-checker (< &real &real))
+(define-comparison-inferrer (< >=))
+
+(define-simple-type-checker (<= &real &real))
+(define-comparison-inferrer (<= >))
+
+(define-simple-type-checker (>= &real &real))
+(define-comparison-inferrer (>= <))
+
+(define-simple-type-checker (> &real &real))
+(define-comparison-inferrer (> <=))
+
+;; Arithmetic.
+(define-syntax-rule (define-unary-result! a result min max)
+ (let ((min* min)
+ (max* max)
+ (type (logand (&type a) &number)))
+ (cond
+ ((not (= type (&type a)))
+ ;; Not a number. Punt and do nothing.
+ (define! result &all-types -inf.0 +inf.0))
+ ;; Complex numbers don't have a range.
+ ((eqv? type &complex)
+ (define! result &complex -inf.0 +inf.0))
+ (else
+ (define! result type min* max*)))))
+
+(define-syntax-rule (define-binary-result! a b result closed? min max)
+ (let ((min* min)
+ (max* max)
+ (a-type (logand (&type a) &number))
+ (b-type (logand (&type b) &number)))
+ (cond
+ ((or (not (= a-type (&type a))) (not (= b-type (&type b))))
+ ;; One input not a number. Perhaps we end up dispatching to
+ ;; GOOPS.
+ (define! result &all-types -inf.0 +inf.0))
+ ;; Complex and floating-point numbers are contagious.
+ ((or (eqv? a-type &complex) (eqv? b-type &complex))
+ (define! result &complex -inf.0 +inf.0))
+ ((or (eqv? a-type &flonum) (eqv? b-type &flonum))
+ (define! result &flonum min* max*))
+ ;; Exact integers are closed under some operations.
+ ((and closed? (eqv? a-type &exact-integer) (eqv? b-type &exact-integer))
+ (define! result &exact-integer min* max*))
+ (else
+ ;; Fractions may become integers.
+ (let ((type (logior a-type b-type)))
+ (define! result
+ (if (zero? (logand type &fraction))
+ type
+ (logior type &exact-integer))
+ min* max*))))))
+
+(define-simple-type-checker (add &number &number))
+(define-type-inferrer (add a b result)
+ (define-binary-result! a b result #t
+ (+ (&min a) (&min b))
+ (+ (&max a) (&max b))))
+
+(define-simple-type-checker (sub &number &number))
+(define-type-inferrer (sub a b result)
+ (define-binary-result! a b result #t
+ (- (&min a) (&max b))
+ (- (&max a) (&min b))))
+
+(define-simple-type-checker (mul &number &number))
+(define-type-inferrer (mul a b result)
+ (let ((min-a (&min a)) (max-a (&max a))
+ (min-b (&min b)) (max-b (&max b))
+ ;; We only really get +inf.0 at runtime for flonums and
+ ;; compnums. If we have inferred that the arguments are not
+ ;; flonums and not compnums, then the result of (* +inf.0 0) at
+ ;; range inference time is 0 and not +nan.0.
+ (nan-impossible? (not (logtest (logior (&type a) (&type b))
+ (logior &flonum &complex)))))
+ (define (nan* a b)
+ (if (and (or (and (inf? a) (zero? b))
+ (and (zero? a) (inf? b)))
+ nan-impossible?)
+ 0
+ (* a b)))
+ (let ((-- (nan* min-a min-b))
+ (-+ (nan* min-a max-b))
+ (++ (nan* max-a max-b))
+ (+- (nan* max-a min-b)))
+ (let ((has-nan? (or (nan? --) (nan? -+) (nan? ++) (nan? +-))))
+ (define-binary-result! a b result #t
+ (cond
+ ((eqv? a b) 0)
+ (has-nan? -inf.0)
+ (else (min -- -+ ++ +-)))
+ (if has-nan?
+ +inf.0
+ (max -- -+ ++ +-)))))))
+
+(define-type-checker (div a b)
+ (and (check-type a &number -inf.0 +inf.0)
+ (check-type b &number -inf.0 +inf.0)
+ ;; We only know that there will not be an exception if b is not
+ ;; zero.
+ (not (<= (&min b) 0 (&max b)))))
+(define-type-inferrer (div a b result)
+ (let ((min-a (&min a)) (max-a (&max a))
+ (min-b (&min b)) (max-b (&max b)))
+ (call-with-values
+ (lambda ()
+ (if (<= min-b 0 max-b)
+ ;; If the range of the divisor crosses 0, the result spans
+ ;; the whole range.
+ (values -inf.0 +inf.0)
+ ;; Otherwise min-b and max-b have the same sign, and cannot both
+ ;; be infinity.
+ (let ((--- (if (inf? min-b) 0 (floor/ min-a min-b)))
+ (-+- (if (inf? max-b) 0 (floor/ min-a max-b)))
+ (++- (if (inf? max-b) 0 (floor/ max-a max-b)))
+ (+-- (if (inf? min-b) 0 (floor/ max-a min-b)))
+ (--+ (if (inf? min-b) 0 (ceiling/ min-a min-b)))
+ (-++ (if (inf? max-b) 0 (ceiling/ min-a max-b)))
+ (+++ (if (inf? max-b) 0 (ceiling/ max-a max-b)))
+ (+-+ (if (inf? min-b) 0 (ceiling/ max-a min-b))))
+ (values (min (min --- -+- ++- +--)
+ (min --+ -++ +++ +-+))
+ (max (max --- -+- ++- +--)
+ (max --+ -++ +++ +-+))))))
+ (lambda (min max)
+ (define-binary-result! a b result #f min max)))))
+
+(define-simple-type-checker (add1 &number))
+(define-type-inferrer (add1 a result)
+ (define-unary-result! a result (1+ (&min a)) (1+ (&max a))))
+
+(define-simple-type-checker (sub1 &number))
+(define-type-inferrer (sub1 a result)
+ (define-unary-result! a result (1- (&min a)) (1- (&max a))))
+
+(define-type-checker (quo a b)
+ (and (check-type a &exact-integer -inf.0 +inf.0)
+ (check-type b &exact-integer -inf.0 +inf.0)
+ ;; We only know that there will not be an exception if b is not
+ ;; zero.
+ (not (<= (&min b) 0 (&max b)))))
+(define-type-inferrer (quo a b result)
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0)
+ (define! result &exact-integer -inf.0 +inf.0))
+
+(define-type-checker-aliases quo rem)
+(define-type-inferrer (rem a b result)
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0)
+ ;; Same sign as A.
+ (let ((max-abs-rem (1- (max (abs (&min b)) (abs (&max b))))))
+ (cond
+ ((< (&min a) 0)
+ (if (< 0 (&max a))
+ (define! result &exact-integer (- max-abs-rem) max-abs-rem)
+ (define! result &exact-integer (- max-abs-rem) 0)))
+ (else
+ (define! result &exact-integer 0 max-abs-rem)))))
+
+(define-type-checker-aliases quo mod)
+(define-type-inferrer (mod a b result)
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0)
+ ;; Same sign as B.
+ (let ((max-abs-mod (1- (max (abs (&min b)) (abs (&max b))))))
+ (cond
+ ((< (&min b) 0)
+ (if (< 0 (&max b))
+ (define! result &exact-integer (- max-abs-mod) max-abs-mod)
+ (define! result &exact-integer (- max-abs-mod) 0)))
+ (else
+ (define! result &exact-integer 0 max-abs-mod)))))
+
+;; Predicates.
+(define-syntax-rule (define-number-kind-predicate-inferrer name type)
+ (define-type-inferrer (name val result)
+ (cond
+ ((zero? (logand (&type val) type))
+ (define! result &false 0 0))
+ ((zero? (logand (&type val) (lognot type)))
+ (define! result &true 0 0))
+ (else
+ (define! result (logior &true &false) 0 0)))))
+(define-number-kind-predicate-inferrer complex? &number)
+(define-number-kind-predicate-inferrer real? &real)
+(define-number-kind-predicate-inferrer rational?
+ (logior &exact-integer &fraction))
+(define-number-kind-predicate-inferrer integer?
+ (logior &exact-integer &flonum))
+(define-number-kind-predicate-inferrer exact-integer?
+ &exact-integer)
+
+(define-simple-type-checker (exact? &number))
+(define-type-inferrer (exact? val result)
+ (restrict! val &number -inf.0 +inf.0)
+ (cond
+ ((zero? (logand (&type val) (logior &exact-integer &fraction)))
+ (define! result &false 0 0))
+ ((zero? (logand (&type val) (lognot (logior &exact-integer &fraction))))
+ (define! result &true 0 0))
+ (else
+ (define! result (logior &true &false) 0 0))))
+
+(define-simple-type-checker (inexact? &number))
+(define-type-inferrer (inexact? val result)
+ (restrict! val &number -inf.0 +inf.0)
+ (cond
+ ((zero? (logand (&type val) (logior &flonum &complex)))
+ (define! result &false 0 0))
+ ((zero? (logand (&type val) (logand &number
+ (lognot (logior &flonum &complex)))))
+ (define! result &true 0 0))
+ (else
+ (define! result (logior &true &false) 0 0))))
+
+(define-simple-type-checker (inf? &real))
+(define-type-inferrer (inf? val result)
+ (restrict! val &real -inf.0 +inf.0)
+ (cond
+ ((or (zero? (logand (&type val) (logior &flonum &complex)))
+ (and (not (inf? (&min val))) (not (inf? (&max val)))))
+ (define! result &false 0 0))
+ (else
+ (define! result (logior &true &false) 0 0))))
+
+(define-type-aliases inf? nan?)
+
+(define-simple-type (even? &exact-integer)
+ ((logior &true &false) 0 0))
+(define-type-aliases even? odd?)
+
+;; Bit operations.
+(define-simple-type-checker (ash &exact-integer &exact-integer))
+(define-type-inferrer (ash val count result)
+ (define (ash* val count)
+ ;; As we can only represent a 32-bit range, don't bother inferring
+ ;; shifts that might exceed that range.
+ (cond
+ ((inf? val) val) ; Preserves sign.
+ ((< -32 count 32) (ash val count))
+ ((zero? val) 0)
+ ((positive? val) +inf.0)
+ (else -inf.0)))
+ (restrict! val &exact-integer -inf.0 +inf.0)
+ (restrict! count &exact-integer -inf.0 +inf.0)
+ (let ((-- (ash* (&min val) (&min count)))
+ (-+ (ash* (&min val) (&max count)))
+ (++ (ash* (&max val) (&max count)))
+ (+- (ash* (&max val) (&min count))))
+ (define! result &exact-integer
+ (min -- -+ ++ +-)
+ (max -- -+ ++ +-))))
+
+(define (next-power-of-two n)
+ (let lp ((out 1))
+ (if (< n out)
+ out
+ (lp (ash out 1)))))
+
+(define-simple-type-checker (logand &exact-integer &exact-integer))
+(define-type-inferrer (logand a b result)
+ (define (logand-min a b)
+ (if (and (negative? a) (negative? b))
+ (min a b)
+ 0))
+ (define (logand-max a b)
+ (if (and (positive? a) (positive? b))
+ (min a b)
+ 0))
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0)
+ (define! result &exact-integer
+ (logand-min (&min a) (&min b))
+ (logand-max (&max a) (&max b))))
+
+(define-simple-type-checker (logior &exact-integer &exact-integer))
+(define-type-inferrer (logior a b result)
+ ;; Saturate all bits of val.
+ (define (saturate val)
+ (1- (next-power-of-two val)))
+ (define (logior-min a b)
+ (cond ((and (< a 0) (<= 0 b)) a)
+ ((and (< b 0) (<= 0 a)) b)
+ (else (max a b))))
+ (define (logior-max a b)
+ ;; If either operand is negative, just assume the max is -1.
+ (cond
+ ((or (< a 0) (< b 0)) -1)
+ ((or (inf? a) (inf? b)) +inf.0)
+ (else (saturate (logior a b)))))
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0)
+ (define! result &exact-integer
+ (logior-min (&min a) (&min b))
+ (logior-max (&max a) (&max b))))
+
+;; For our purposes, treat logxor the same as logior.
+(define-type-aliases logior logxor)
+
+(define-simple-type-checker (lognot &exact-integer))
+(define-type-inferrer (lognot a result)
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (define! result &exact-integer
+ (- -1 (&max a))
+ (- -1 (&min a))))
+
+(define-simple-type-checker (logtest &exact-integer &exact-integer))
+(define-predicate-inferrer (logtest a b true?)
+ (restrict! a &exact-integer -inf.0 +inf.0)
+ (restrict! b &exact-integer -inf.0 +inf.0))
+
+(define-simple-type-checker (logbit? (&exact-integer 0 +inf.0) &exact-integer))
+(define-type-inferrer (logbit? a b result)
+ (let ((a-min (&min a))
+ (a-max (&max a))
+ (b-min (&min b))
+ (b-max (&max b)))
+ (if (and (eqv? a-min a-max) (>= a-min 0) (not (inf? a-min))
+ (eqv? b-min b-max) (>= b-min 0) (not (inf? b-min)))
+ (let ((type (if (logbit? a-min b-min) &true &false)))
+ (define! result type 0 0))
+ (define! result (logior &true &false) 0 0))))
+
+;; Flonums.
+(define-simple-type-checker (sqrt &number))
+(define-type-inferrer (sqrt x result)
+ (let ((type (&type x)))
+ (cond
+ ((and (zero? (logand type &complex)) (<= 0 (&min x)))
+ (define! result
+ (logior type &flonum)
+ (inexact->exact (floor (sqrt (&min x))))
+ (if (inf? (&max x))
+ +inf.0
+ (inexact->exact (ceiling (sqrt (&max x)))))))
+ (else
+ (define! result (logior type &flonum &complex) -inf.0 +inf.0)))))
+
+(define-simple-type-checker (abs &real))
+(define-type-inferrer (abs x result)
+ (let ((type (&type x)))
+ (cond
+ ((eqv? type (logand type &number))
+ (restrict! x &real -inf.0 +inf.0)
+ (define! result (logand type &real)
+ (min (abs (&min x)) (abs (&max x)))
+ (max (abs (&min x)) (abs (&max x)))))
+ (else
+ (define! result (logior (logand (&type x) (lognot &number))
+ (logand (&type x) &real))
+ (max (&min x) 0)
+ (max (abs (&min x)) (abs (&max x))))))))
+
+
+
+
+;;;
+;;; Characters.
+;;;
+
+(define-simple-type (char &char &char)
+ ((logior &true &false) 0 0))
+(define-type-aliases char char<=? char>=? char>?)
+
+(define-simple-type-checker (integer->char (&exact-integer 0 #x10ffff)))
+(define-type-inferrer (integer->char i result)
+ (restrict! i &exact-integer 0 #x10ffff)
+ (define! result &char (max (&min i) 0) (min (&max i) #x10ffff)))
+
+(define-simple-type-checker (char->integer &char))
+(define-type-inferrer (char->integer c result)
+ (restrict! c &char 0 #x10ffff)
+ (define! result &exact-integer (max (&min c) 0) (min (&max c) #x10ffff)))
+
+
+
+
+;;;
+;;; Type flow analysis: the meet (ahem) of the algorithm.
+;;;
+
+(define (successor-count cont)
+ (match cont
+ (($ $kargs _ _ ($ $continue k src exp))
+ (match exp
+ ((or ($ $branch) ($ $prompt)) 2)
+ (_ 1)))
+ (($ $kfun src meta self tail clause) (if clause 1 0))
+ (($ $kclause arity body alt) (if alt 2 1))
+ (($ $kreceive) 1)
+ (($ $ktail) 0)))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define-syntax-rule (make-worklist-folder* seed ...)
+ (lambda (f worklist seed ...)
+ (let lp ((worklist worklist) (seed seed) ...)
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist i)
+ (if i
+ (call-with-values (lambda () (f i seed ...))
+ (lambda (i* seed ...)
+ (let add ((i* i*) (worklist worklist))
+ (match i*
+ (() (lp worklist seed ...))
+ ((i . i*) (add i* (intset-add worklist i)))))))
+ (values seed ...)))))))
+
+(define worklist-fold*
+ (case-lambda
+ ((f worklist seed)
+ ((make-worklist-folder* seed) f worklist seed))))
+
+(define intmap-ensure
+ (let* ((*absent* (list 'absent))
+ (not-found (lambda (i) *absent*)))
+ (lambda (map i ensure)
+ (let ((val (intmap-ref map i not-found)))
+ (if (eq? val *absent*)
+ (let ((val (ensure i)))
+ (values (intmap-add map i val) val))
+ (values map val))))))
+
+;; For best results, the labels in the function starting should be
+;; topologically sorted (renumbered). Otherwise the backward branch
+;; detection mentioned in the module commentary will trigger for
+;; ordinary forward branches.
+(define (infer-types conts kfun)
+ "Compute types for all variables bound in the function labelled
+@var{kfun}, from @var{conts}. Returns an intmap mapping labels to type
+entries.
+
+A type entry is a vector that describes the types of the values that
+flow into and out of a labelled expressoin. The first slot in the type
+entry vector corresponds to the types that flow in, and the rest of the
+slots correspond to the types that flow out. Each element of the type
+entry vector is an intmap mapping variable name to the variable's
+inferred type. An inferred type is a 3-vector of type, minimum, and
+maximum, where type is a bitset as a fixnum."
+ (define (get-entry typev label) (intmap-ref typev label))
+ (define (entry-not-found label)
+ (make-vector (1+ (successor-count (intmap-ref conts label))) #f))
+ (define (ensure-entry typev label)
+ (intmap-ensure typev label entry-not-found))
+
+ (define (compute-initial-state)
+ (let ((entry (entry-not-found kfun)))
+ ;; Nothing flows in to the first label.
+ (vector-set! entry 0 empty-intmap)
+ (intmap-add empty-intmap kfun entry)))
+
+ (define (adjoin-vars types vars entry)
+ (match vars
+ (() types)
+ ((var . vars)
+ (adjoin-vars (adjoin-var types var entry) vars entry))))
+
+ (define (infer-primcall types succ name args result)
+ (cond
+ ((hashq-ref *type-inferrers* name)
+ => (lambda (inferrer)
+ ;; FIXME: remove the apply?
+ ;; (pk 'primcall name args result)
+ (apply inferrer types succ
+ (if result
+ (append args (list result))
+ args))))
+ (result
+ (adjoin-var types result all-types-entry))
+ (else
+ types)))
+
+ (define (vector-replace vec idx val)
+ (let ((vec (vector-copy vec)))
+ (vector-set! vec idx val)
+ vec))
+
+ (define (update-out-types label typev types succ-idx)
+ (let* ((entry (get-entry typev label))
+ (old-types (vector-ref entry (1+ succ-idx))))
+ (if (eq? types old-types)
+ (values typev #f)
+ (let ((entry (vector-replace entry (1+ succ-idx) types))
+ (first? (not old-types)))
+ (values (intmap-replace typev label entry) first?)))))
+
+ (define (update-in-types label typev types saturate?)
+ (let*-values (((typev entry) (ensure-entry typev label))
+ ((old-types) (vector-ref entry 0))
+ ;; TODO: If the label has only one predecessor, we can
+ ;; avoid the meet.
+ ((types) (if (not old-types)
+ types
+ (let ((meet (if saturate?
+ type-entry-saturating-union
+ type-entry-union)))
+ (intmap-intersect old-types types meet)))))
+ (if (eq? old-types types)
+ (values typev #f)
+ (let ((entry (vector-replace entry 0 types)))
+ (values (intmap-replace typev label entry) #t)))))
+
+ (define (propagate-types label typev succ-idx succ-label types)
+ (let*-values
+ (((typev first?) (update-out-types label typev types succ-idx))
+ ((saturate?) (and (not first?) (<= succ-label label)))
+ ((typev changed?) (update-in-types succ-label typev types saturate?)))
+ (values (if changed? (list succ-label) '()) typev)))
+
+ (define (visit-exp label typev k types exp)
+ (define (propagate1 succ-label types)
+ (propagate-types label typev 0 succ-label types))
+ (define (propagate2 succ0-label types0 succ1-label types1)
+ (let*-values (((changed0 typev)
+ (propagate-types label typev 0 succ0-label types0))
+ ((changed1 typev)
+ (propagate-types label typev 1 succ1-label types1)))
+ (values (append changed0 changed1) typev)))
+ ;; Each of these branches must propagate to its successors.
+ (match exp
+ (($ $branch kt ($ $values (arg)))
+ ;; The "normal" continuation is the #f branch.
+ (let ((kf-types (restrict-var types arg
+ (make-type-entry (logior &false &nil)
+ 0
+ 0)))
+ (kt-types (restrict-var types arg
+ (make-type-entry
+ (logand &all-types
+ (lognot (logior &false &nil)))
+ -inf.0 +inf.0))))
+ (propagate2 k kf-types kt kt-types)))
+ (($ $branch kt ($ $primcall name args))
+ ;; The "normal" continuation is the #f branch.
+ (let ((kf-types (infer-primcall types 0 name args #f))
+ (kt-types (infer-primcall types 1 name args #f)))
+ (propagate2 k kf-types kt kt-types)))
+ (($ $prompt escape? tag handler)
+ ;; The "normal" continuation enters the prompt.
+ (propagate2 k types handler types))
+ (($ $primcall name args)
+ (propagate1 k
+ (match (intmap-ref conts k)
+ (($ $kargs _ defs)
+ (infer-primcall types 0 name args
+ (match defs ((var) var) (() #f))))
+ (_
+ ;; (pk 'warning-no-restrictions name)
+ types))))
+ (($ $values args)
+ (match (intmap-ref conts k)
+ (($ $kargs _ defs)
+ (let ((in types))
+ (let lp ((defs defs) (args args) (out types))
+ (match (cons defs args)
+ ((() . ())
+ (propagate1 k out))
+ (((def . defs) . (arg . args))
+ (lp defs args
+ (adjoin-var out def (var-type-entry in arg))))))))
+ (_
+ (propagate1 k types))))
+ ((or ($ $call) ($ $callk))
+ (propagate1 k types))
+ (($ $rec names vars funs)
+ (let ((proc-type (make-type-entry &procedure -inf.0 +inf.0)))
+ (propagate1 k (adjoin-vars types vars proc-type))))
+ (_
+ (match (intmap-ref conts k)
+ (($ $kargs (_) (var))
+ (let ((entry (match exp
+ (($ $const val)
+ (constant-type val))
+ ((or ($ $prim) ($ $fun) ($ $closure))
+ ;; Could be more precise here.
+ (make-type-entry &procedure -inf.0 +inf.0)))))
+ (propagate1 k (adjoin-var types var entry))))))))
+
+ (define (visit-cont label typev)
+ (let ((types (vector-ref (intmap-ref typev label) 0)))
+ (define (propagate0)
+ (values '() typev))
+ (define (propagate1 succ-label types)
+ (propagate-types label typev 0 succ-label types))
+ (define (propagate2 succ0-label types0 succ1-label types1)
+ (let*-values (((changed0 typev)
+ (propagate-types label typev 0 succ0-label types0))
+ ((changed1 typev)
+ (propagate-types label typev 1 succ1-label types1)))
+ (values (append changed0 changed1) typev)))
+
+ ;; Add types for new definitions, and restrict types of
+ ;; existing variables due to side effects.
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-exp label typev k types exp))
+ (($ $kreceive arity k)
+ (match (intmap-ref conts k)
+ (($ $kargs names vars)
+ (propagate1 k (adjoin-vars types vars all-types-entry)))))
+ (($ $kfun src meta self tail clause)
+ (if clause
+ (propagate1 clause (adjoin-var types self all-types-entry))
+ (propagate0)))
+ (($ $kclause arity kbody kalt)
+ (match (intmap-ref conts kbody)
+ (($ $kargs _ defs)
+ (let ((body-types (adjoin-vars types defs all-types-entry)))
+ (if kalt
+ (propagate2 kbody body-types kalt types)
+ (propagate1 kbody body-types))))))
+ (($ $ktail) (propagate0)))))
+
+ (worklist-fold* visit-cont
+ (intset-add empty-intset kfun)
+ (compute-initial-state)))
+
+(define (lookup-pre-type types label def)
+ (let* ((entry (intmap-ref types label))
+ (tentry (var-type-entry (vector-ref entry 0) def)))
+ (values (type-entry-type tentry)
+ (type-entry-min tentry)
+ (type-entry-max tentry))))
+
+(define (lookup-post-type types label def succ-idx)
+ (let* ((entry (intmap-ref types label))
+ (tentry (var-type-entry (vector-ref entry (1+ succ-idx)) def)))
+ (values (type-entry-type tentry)
+ (type-entry-min tentry)
+ (type-entry-max tentry))))
+
+(define (primcall-types-check? types label name args)
+ (match (hashq-ref *type-checkers* name)
+ (#f #f)
+ (checker
+ (let ((entry (intmap-ref types label)))
+ (apply checker (vector-ref entry 0) args)))))
diff --git a/module/language/cps/utils.scm b/module/language/cps/utils.scm
new file mode 100644
index 000000000..fa4673c4e
--- /dev/null
+++ b/module/language/cps/utils.scm
@@ -0,0 +1,477 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Helper facilities for working with CPS.
+;;;
+;;; Code:
+
+(define-module (language cps utils)
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-11)
+ #:use-module (language cps)
+ #:use-module (language cps intset)
+ #:use-module (language cps intmap)
+ #:export (;; Fresh names.
+ label-counter var-counter
+ fresh-label fresh-var
+ with-fresh-name-state compute-max-label-and-var
+ let-fresh
+
+ ;; Various utilities.
+ fold1 fold2
+ trivial-intset
+ intmap-map
+ intmap-keys
+ invert-bijection invert-partition
+ intset->intmap
+ worklist-fold
+ fixpoint
+
+ ;; Flow analysis.
+ compute-constant-values
+ compute-function-body
+ compute-reachable-functions
+ compute-successors
+ invert-graph
+ compute-predecessors
+ compute-reverse-post-order
+ compute-strongly-connected-components
+ compute-sorted-strongly-connected-components
+ compute-idoms
+ compute-dom-edges
+ ))
+
+(define label-counter (make-parameter #f))
+(define var-counter (make-parameter #f))
+
+(define (fresh-label)
+ (let ((count (or (label-counter)
+ (error "fresh-label outside with-fresh-name-state"))))
+ (label-counter (1+ count))
+ count))
+
+(define (fresh-var)
+ (let ((count (or (var-counter)
+ (error "fresh-var outside with-fresh-name-state"))))
+ (var-counter (1+ count))
+ count))
+
+(define-syntax-rule (let-fresh (label ...) (var ...) body ...)
+ (let* ((label (fresh-label)) ...
+ (var (fresh-var)) ...)
+ body ...))
+
+(define-syntax-rule (with-fresh-name-state fun body ...)
+ (call-with-values (lambda () (compute-max-label-and-var fun))
+ (lambda (max-label max-var)
+ (parameterize ((label-counter (1+ max-label))
+ (var-counter (1+ max-var)))
+ body ...))))
+
+(define (compute-max-label-and-var conts)
+ (values (or (intmap-prev conts) -1)
+ (intmap-fold (lambda (k cont max-var)
+ (match cont
+ (($ $kargs names syms body)
+ (apply max max-var syms))
+ (($ $kfun src meta self)
+ (max max-var self))
+ (_ max-var)))
+ conts
+ -1)))
+
+(define-inlinable (fold1 f l s0)
+ (let lp ((l l) (s0 s0))
+ (match l
+ (() s0)
+ ((elt . l) (lp l (f elt s0))))))
+
+(define-inlinable (fold2 f l s0 s1)
+ (let lp ((l l) (s0 s0) (s1 s1))
+ (match l
+ (() (values s0 s1))
+ ((elt . l)
+ (call-with-values (lambda () (f elt s0 s1))
+ (lambda (s0 s1)
+ (lp l s0 s1)))))))
+
+(define (trivial-intset set)
+ "Returns the sole member of @var{set}, if @var{set} has exactly one
+member, or @code{#f} otherwise."
+ (let ((first (intset-next set)))
+ (and first
+ (not (intset-next set (1+ first)))
+ first)))
+
+(define (intmap-map proc map)
+ (persistent-intmap
+ (intmap-fold (lambda (k v out) (intmap-replace! out k (proc k v)))
+ map
+ map)))
+
+(define (intmap-keys map)
+ "Return an intset of the keys in @var{map}."
+ (persistent-intset
+ (intmap-fold (lambda (k v keys) (intset-add! keys k)) map empty-intset)))
+
+(define (invert-bijection map)
+ "Assuming the values of @var{map} are integers and are unique, compute
+a map in which each value maps to its key. If the values are not
+unique, an error will be signalled."
+ (intmap-fold (lambda (k v out) (intmap-add out v k)) map empty-intmap))
+
+(define (invert-partition map)
+ "Assuming the values of @var{map} are disjoint intsets, compute a map
+in which each member of each set maps to its key. If the values are not
+disjoint, an error will be signalled."
+ (intmap-fold (lambda (k v* out)
+ (intset-fold (lambda (v out) (intmap-add out v k)) v* out))
+ map empty-intmap))
+
+(define (intset->intmap f set)
+ (persistent-intmap
+ (intset-fold (lambda (label preds)
+ (intmap-add! preds label (f label)))
+ set empty-intmap)))
+
+(define worklist-fold
+ (case-lambda
+ ((f in out)
+ (let lp ((in in) (out out))
+ (if (eq? in empty-intset)
+ out
+ (call-with-values (lambda () (f in out)) lp))))
+ ((f in out0 out1)
+ (let lp ((in in) (out0 out0) (out1 out1))
+ (if (eq? in empty-intset)
+ (values out0 out1)
+ (call-with-values (lambda () (f in out0 out1)) lp))))))
+
+(define fixpoint
+ (case-lambda
+ ((f x)
+ (let lp ((x x))
+ (let ((x* (f x)))
+ (if (eq? x x*) x* (lp x*)))))
+ ((f x0 x1)
+ (let lp ((x0 x0) (x1 x1))
+ (call-with-values (lambda () (f x0 x1))
+ (lambda (x0* x1*)
+ (if (and (eq? x0 x0*) (eq? x1 x1*))
+ (values x0* x1*)
+ (lp x0* x1*))))))))
+
+(define (compute-defining-expressions conts)
+ (define (meet-defining-expressions old new)
+ ;; If there are multiple definitions, punt and
+ ;; record #f.
+ #f)
+ (persistent-intmap
+ (intmap-fold (lambda (label cont defs)
+ (match cont
+ (($ $kargs _ _ ($ $continue k src exp))
+ (match (intmap-ref conts k)
+ (($ $kargs (_) (var))
+ (intmap-add! defs var exp meet-defining-expressions))
+ (_ defs)))
+ (_ defs)))
+ conts
+ empty-intmap)))
+
+(define (compute-constant-values conts)
+ (persistent-intmap
+ (intmap-fold (lambda (var exp out)
+ (match exp
+ (($ $const val)
+ (intmap-add! out var val))
+ (_ out)))
+ (compute-defining-expressions conts)
+ empty-intmap)))
+
+(define (compute-function-body conts kfun)
+ (persistent-intset
+ (let visit-cont ((label kfun) (labels empty-intset))
+ (cond
+ ((intset-ref labels label) labels)
+ (else
+ (let ((labels (intset-add! labels label)))
+ (match (intmap-ref conts label)
+ (($ $kreceive arity k) (visit-cont k labels))
+ (($ $kfun src meta self ktail kclause)
+ (let ((labels (visit-cont ktail labels)))
+ (if kclause
+ (visit-cont kclause labels)
+ labels)))
+ (($ $ktail) labels)
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (visit-cont kalt (visit-cont kbody labels))
+ (visit-cont kbody labels)))
+ (($ $kargs names syms ($ $continue k src exp))
+ (visit-cont k (match exp
+ (($ $branch k)
+ (visit-cont k labels))
+ (($ $prompt escape? tag k)
+ (visit-cont k labels))
+ (_ labels)))))))))))
+
+(define (compute-reachable-functions conts kfun)
+ "Compute a mapping LABEL->LABEL..., where each key is a reachable
+$kfun and each associated value is the body of the function, as an
+intset."
+ (define (intset-cons i set) (intset-add set i))
+ (define (visit-fun kfun body to-visit)
+ (intset-fold
+ (lambda (label to-visit)
+ (define (return kfun*) (fold intset-cons to-visit kfun*))
+ (define (return1 kfun) (intset-add to-visit kfun))
+ (define (return0) to-visit)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $fun label) (return1 label))
+ (($ $rec _ _ (($ $fun labels) ...)) (return labels))
+ (($ $closure label nfree) (return1 label))
+ (($ $callk label) (return1 label))
+ (_ (return0))))
+ (_ (return0))))
+ body
+ to-visit))
+ (let lp ((to-visit (intset kfun)) (visited empty-intmap))
+ (let ((to-visit (intset-subtract to-visit (intmap-keys visited))))
+ (if (eq? to-visit empty-intset)
+ visited
+ (call-with-values
+ (lambda ()
+ (intset-fold
+ (lambda (kfun to-visit visited)
+ (let ((body (compute-function-body conts kfun)))
+ (values (visit-fun kfun body to-visit)
+ (intmap-add visited kfun body))))
+ to-visit
+ empty-intset
+ visited))
+ lp)))))
+
+(define* (compute-successors conts #:optional (kfun (intmap-next conts)))
+ (define (visit label succs)
+ (let visit ((label kfun) (succs empty-intmap))
+ (define (propagate0)
+ (intmap-add! succs label empty-intset))
+ (define (propagate1 succ)
+ (visit succ (intmap-add! succs label (intset succ))))
+ (define (propagate2 succ0 succ1)
+ (let ((succs (intmap-add! succs label (intset succ0 succ1))))
+ (visit succ1 (visit succ0 succs))))
+ (if (intmap-ref succs label (lambda (_) #f))
+ succs
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (match exp
+ (($ $branch kt) (propagate2 k kt))
+ (($ $prompt escape? tag handler) (propagate2 k handler))
+ (_ (propagate1 k))))
+ (($ $kreceive arity k)
+ (propagate1 k))
+ (($ $kfun src meta self tail clause)
+ (if clause
+ (propagate2 clause tail)
+ (propagate1 tail)))
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (propagate2 kbody kalt)
+ (propagate1 kbody)))
+ (($ $ktail) (propagate0))))))
+ (persistent-intmap (visit kfun empty-intmap)))
+
+(define* (compute-predecessors conts kfun #:key
+ (labels (compute-function-body conts kfun)))
+ (define (meet cdr car)
+ (cons car cdr))
+ (define (add-preds label preds)
+ (define (add-pred k preds)
+ (intmap-add! preds k label meet))
+ (match (intmap-ref conts label)
+ (($ $kreceive arity k)
+ (add-pred k preds))
+ (($ $kfun src meta self ktail kclause)
+ (add-pred ktail (if kclause (add-pred kclause preds) preds)))
+ (($ $ktail)
+ preds)
+ (($ $kclause arity kbody kalt)
+ (add-pred kbody (if kalt (add-pred kalt preds) preds)))
+ (($ $kargs names syms ($ $continue k src exp))
+ (add-pred k
+ (match exp
+ (($ $branch k) (add-pred k preds))
+ (($ $prompt _ _ k) (add-pred k preds))
+ (_ preds))))))
+ (persistent-intmap
+ (intset-fold add-preds labels
+ (intset->intmap (lambda (label) '()) labels))))
+
+(define (compute-reverse-post-order succs start)
+ "Compute a reverse post-order numbering for a depth-first walk over
+nodes reachable from the start node."
+ (let visit ((label start) (order '()) (visited empty-intset))
+ (call-with-values
+ (lambda ()
+ (intset-fold (lambda (succ order visited)
+ (if (intset-ref visited succ)
+ (values order visited)
+ (visit succ order visited)))
+ (intmap-ref succs label)
+ order
+ (intset-add! visited label)))
+ (lambda (order visited)
+ ;; After visiting successors, add label to the reverse post-order.
+ (values (cons label order) visited)))))
+
+(define (invert-graph succs)
+ "Given a graph PRED->SUCC..., where PRED is a label and SUCC... is an
+intset of successors, return a graph SUCC->PRED...."
+ (intmap-fold (lambda (pred succs preds)
+ (intset-fold
+ (lambda (succ preds)
+ (intmap-add preds succ pred intset-add))
+ succs
+ preds))
+ succs
+ (intmap-map (lambda (label _) empty-intset) succs)))
+
+(define (compute-strongly-connected-components succs start)
+ "Given a LABEL->SUCCESSOR... graph, compute a SCC->LABEL... map
+partitioning the labels into strongly connected components (SCCs)."
+ (let ((preds (invert-graph succs)))
+ (define (visit-scc scc sccs-by-label)
+ (let visit ((label scc) (sccs-by-label sccs-by-label))
+ (if (intmap-ref sccs-by-label label (lambda (_) #f))
+ sccs-by-label
+ (intset-fold visit
+ (intmap-ref preds label)
+ (intmap-add sccs-by-label label scc)))))
+ (intmap-fold
+ (lambda (label scc sccs)
+ (let ((labels (intset-add empty-intset label)))
+ (intmap-add sccs scc labels intset-union)))
+ (fold visit-scc empty-intmap (compute-reverse-post-order succs start))
+ empty-intmap)))
+
+(define (compute-sorted-strongly-connected-components edges)
+ "Given a LABEL->SUCCESSOR... graph, return a list of strongly
+connected components in sorted order."
+ (define nodes
+ (intmap-keys edges))
+ ;; Add a "start" node that links to all nodes in the graph, and then
+ ;; remove it from the result.
+ (define start
+ (if (eq? nodes empty-intset)
+ 0
+ (1+ (intset-prev nodes))))
+ (define components
+ (intmap-remove
+ (compute-strongly-connected-components (intmap-add edges start nodes)
+ start)
+ start))
+ (define node-components
+ (intmap-fold (lambda (id nodes out)
+ (intset-fold (lambda (node out) (intmap-add out node id))
+ nodes out))
+ components
+ empty-intmap))
+ (define (node-component node)
+ (intmap-ref node-components node))
+ (define (component-successors id nodes)
+ (intset-remove
+ (intset-fold (lambda (node out)
+ (intset-fold
+ (lambda (successor out)
+ (intset-add out (node-component successor)))
+ (intmap-ref edges node)
+ out))
+ nodes
+ empty-intset)
+ id))
+ (define component-edges
+ (intmap-map component-successors components))
+ (define preds
+ (invert-graph component-edges))
+ (define roots
+ (intmap-fold (lambda (id succs out)
+ (if (eq? empty-intset succs)
+ (intset-add out id)
+ out))
+ component-edges
+ empty-intset))
+ ;; As above, add a "start" node that links to the roots, and remove it
+ ;; from the result.
+ (match (compute-reverse-post-order (intmap-add preds start roots) start)
+ (((? (lambda (id) (eqv? id start))) . ids)
+ (map (lambda (id) (intmap-ref components id)) ids))))
+
+;; Precondition: For each function in CONTS, the continuation names are
+;; topologically sorted.
+(define (compute-idoms conts kfun)
+ ;; This is the iterative O(n^2) fixpoint algorithm, originally from
+ ;; Allen and Cocke ("Graph-theoretic constructs for program flow
+ ;; analysis", 1972). See the discussion in Cooper, Harvey, and
+ ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
+ (let ((preds-map (compute-predecessors conts kfun)))
+ (define (compute-idom idoms preds)
+ (define (idom-ref label)
+ (intmap-ref idoms label (lambda (_) #f)))
+ (match preds
+ (() -1)
+ ((pred) pred) ; Shortcut.
+ ((pred . preds)
+ (define (common-idom d0 d1)
+ ;; We exploit the fact that a reverse post-order is a
+ ;; topological sort, and so the idom of a node is always
+ ;; numerically less than the node itself.
+ (let lp ((d0 d0) (d1 d1))
+ (cond
+ ;; d0 or d1 can be false on the first iteration.
+ ((not d0) d1)
+ ((not d1) d0)
+ ((= d0 d1) d0)
+ ((< d0 d1) (lp d0 (idom-ref d1)))
+ (else (lp (idom-ref d0) d1)))))
+ (fold1 common-idom preds pred))))
+ (define (adjoin-idom label preds idoms)
+ (let ((idom (compute-idom idoms preds)))
+ ;; Don't use intmap-add! here.
+ (intmap-add idoms label idom (lambda (old new) new))))
+ (fixpoint (lambda (idoms)
+ (intmap-fold adjoin-idom preds-map idoms))
+ empty-intmap)))
+
+;; Compute a vector containing, for each node, a list of the nodes that
+;; it immediately dominates. These are the "D" edges in the DJ tree.
+(define (compute-dom-edges idoms)
+ (define (snoc cdr car) (cons car cdr))
+ (persistent-intmap
+ (intmap-fold (lambda (label idom doms)
+ (let ((doms (intmap-add! doms label '())))
+ (cond
+ ((< idom 0) doms) ;; No edge to entry.
+ (else (intmap-add! doms idom label snoc)))))
+ idoms
+ empty-intmap)))
diff --git a/module/language/cps/verify.scm b/module/language/cps/verify.scm
new file mode 100644
index 000000000..f4413af0d
--- /dev/null
+++ b/module/language/cps/verify.scm
@@ -0,0 +1,306 @@
+;;; Diagnostic checker for CPS
+;;; Copyright (C) 2014, 2015 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 program. If not, see
+;;; .
+
+;;; Commentary:
+;;;
+;;; A routine to detect invalid CPS.
+;;;
+;;; Code:
+
+(define-module (language cps verify)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:use-module (language cps primitives)
+ #:use-module (srfi srfi-11)
+ #:export (verify))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define-syntax-rule (make-worklist-folder* seed ...)
+ (lambda (f worklist seed ...)
+ (let lp ((worklist worklist) (seed seed) ...)
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist i)
+ (if i
+ (call-with-values (lambda () (f i seed ...))
+ (lambda (i* seed ...)
+ (let add ((i* i*) (worklist worklist))
+ (match i*
+ (() (lp worklist seed ...))
+ ((i . i*) (add i* (intset-add worklist i)))))))
+ (values seed ...)))))))
+
+(define worklist-fold*
+ (case-lambda
+ ((f worklist seed)
+ ((make-worklist-folder* seed) f worklist seed))))
+
+(define (check-distinct-vars conts)
+ (define (adjoin-def var seen)
+ (when (intset-ref seen var)
+ (error "duplicate var name" seen var))
+ (intset-add seen var))
+ (intmap-fold
+ (lambda (label cont seen)
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (fold1 adjoin-def vars seen))
+ (($ $kfun src meta self tail clause)
+ (adjoin-def self seen))
+ (_ seen))
+ )
+ conts
+ empty-intset))
+
+(define (compute-available-definitions conts kfun)
+ "Compute and return a map of LABEL->VAR..., where VAR... are the
+definitions that are available at LABEL."
+ (define (adjoin-def var defs)
+ (when (intset-ref defs var)
+ (error "var already present in defs" defs var))
+ (intset-add defs var))
+
+ (define (propagate defs succ out)
+ (let* ((in (intmap-ref defs succ (lambda (_) #f)))
+ (in* (if in (intset-intersect in out) out)))
+ (if (eq? in in*)
+ (values '() defs)
+ (values (list succ)
+ (intmap-add defs succ in* (lambda (old new) new))))))
+
+ (define (visit-cont label defs)
+ (let ((in (intmap-ref defs label)))
+ (define (propagate0 out)
+ (values '() defs))
+ (define (propagate1 succ out)
+ (propagate defs succ out))
+ (define (propagate2 succ0 succ1 out)
+ (let*-values (((changed0 defs) (propagate defs succ0 out))
+ ((changed1 defs) (propagate defs succ1 out)))
+ (values (append changed0 changed1) defs)))
+
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (let ((out (fold1 adjoin-def vars in)))
+ (match exp
+ (($ $branch kt) (propagate2 k kt out))
+ (($ $prompt escape? tag handler) (propagate2 k handler out))
+ (_ (propagate1 k out)))))
+ (($ $kreceive arity k)
+ (propagate1 k in))
+ (($ $kfun src meta self tail clause)
+ (let ((out (adjoin-def self in)))
+ (if clause
+ (propagate1 clause out)
+ (propagate0 out))))
+ (($ $kclause arity kbody kalt)
+ (if kalt
+ (propagate2 kbody kalt in)
+ (propagate1 kbody in)))
+ (($ $ktail) (propagate0 in)))))
+
+ (worklist-fold* visit-cont
+ (intset kfun)
+ (intmap-add empty-intmap kfun empty-intset)))
+
+(define (intmap-for-each f map)
+ (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
+
+(define (check-valid-var-uses conts kfun)
+ (define (adjoin-def var defs) (intset-add defs var))
+ (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
+ (define (visit-exp exp bound first-order)
+ (define (check-use var)
+ (unless (intset-ref bound var)
+ (error "unbound var" var)))
+ (define (visit-first-order kfun)
+ (if (intset-ref first-order kfun)
+ first-order
+ (visit-fun kfun empty-intset (intset-add first-order kfun))))
+ (match exp
+ ((or ($ $const) ($ $prim)) first-order)
+ ;; todo: $closure
+ (($ $fun kfun)
+ (visit-fun kfun bound first-order))
+ (($ $closure kfun)
+ (visit-first-order kfun))
+ (($ $rec names vars (($ $fun kfuns) ...))
+ (let ((bound (fold1 adjoin-def vars bound)))
+ (fold1 (lambda (kfun first-order)
+ (visit-fun kfun bound first-order))
+ kfuns first-order)))
+ (($ $values args)
+ (for-each check-use args)
+ first-order)
+ (($ $call proc args)
+ (check-use proc)
+ (for-each check-use args)
+ first-order)
+ (($ $callk kfun proc args)
+ (check-use proc)
+ (for-each check-use args)
+ (visit-first-order kfun))
+ (($ $branch kt ($ $values (arg)))
+ (check-use arg)
+ first-order)
+ (($ $branch kt ($ $primcall name args))
+ (for-each check-use args)
+ first-order)
+ (($ $primcall name args)
+ (for-each check-use args)
+ first-order)
+ (($ $prompt escape? tag handler)
+ (check-use tag)
+ first-order)))
+ (intmap-fold
+ (lambda (label bound first-order)
+ (let ((bound (intset-union free bound)))
+ (match (intmap-ref conts label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (visit-exp exp (fold1 adjoin-def vars bound) first-order))
+ (_ first-order))))
+ (compute-available-definitions conts kfun)
+ first-order)))
+
+(define (check-label-partition conts kfun)
+ ;; A continuation can only belong to one function.
+ (intmap-fold
+ (lambda (kfun body seen)
+ (intset-fold
+ (lambda (label seen)
+ (intmap-add seen label kfun
+ (lambda (old new)
+ (error "label used by two functions" label old new))))
+ body
+ seen))
+ (compute-reachable-functions conts kfun)
+ empty-intmap))
+
+(define (compute-reachable-labels conts kfun)
+ (intmap-fold (lambda (kfun body seen) (intset-union seen body))
+ (compute-reachable-functions conts kfun)
+ empty-intset))
+
+(define (check-arities conts kfun)
+ (define (check-arity exp cont)
+ (define (assert-unary)
+ (match cont
+ (($ $kargs (_) (_)) #t)
+ (_ (error "expected unary continuation" cont))))
+ (define (assert-nullary)
+ (match cont
+ (($ $kargs () ()) #t)
+ (_ (error "expected unary continuation" cont))))
+ (define (assert-n-ary n)
+ (match cont
+ (($ $kargs names vars)
+ (unless (= (length vars) n)
+ (error "expected n-ary continuation" n cont)))
+ (_ (error "expected $kargs continuation" cont))))
+ (define (assert-kreceive-or-ktail)
+ (match cont
+ ((or ($ $kreceive) ($ $ktail)) #t)
+ (_ (error "expected $kreceive or $ktail continuation" cont))))
+ (match exp
+ ((or ($ $const) ($ $prim) ($ $closure) ($ $fun))
+ (assert-unary))
+ (($ $rec names vars funs)
+ (unless (= (length names) (length vars) (length funs))
+ (error "invalid $rec" exp))
+ (assert-n-ary (length names))
+ (match cont
+ (($ $kargs names vars*)
+ (unless (equal? vars* vars)
+ (error "bound variable mismatch" vars vars*)))))
+ (($ $values args)
+ (match cont
+ (($ $ktail) #t)
+ (_ (assert-n-ary (length args)))))
+ (($ $call proc args)
+ (assert-kreceive-or-ktail))
+ (($ $callk k proc args)
+ (assert-kreceive-or-ktail))
+ (($ $branch kt exp)
+ (assert-nullary)
+ (match (intmap-ref conts kt)
+ (($ $kargs () ()) #t)
+ (cont (error "bad kt" cont))))
+ (($ $primcall name args)
+ (match cont
+ (($ $kargs names)
+ (match (prim-arity name)
+ ((out . in)
+ (unless (= in (length args))
+ (error "bad arity to primcall" name args in))
+ (unless (= out (length names))
+ (error "bad return arity from primcall" name names out)))))
+ (($ $kreceive)
+ (when (false-if-exception (prim-arity name))
+ (error "primitive should continue to $kargs, not $kreceive" name)))
+ (($ $ktail)
+ (unless (eq? name 'return)
+ (when (false-if-exception (prim-arity name))
+ (error "primitive should continue to $kargs, not $ktail" name))))))
+ (($ $prompt escape? tag handler)
+ (assert-nullary)
+ (match (intmap-ref conts handler)
+ (($ $kreceive) #t)
+ (cont (error "bad handler" cont))))))
+ (let ((reachable (compute-reachable-labels conts kfun)))
+ (intmap-for-each
+ (lambda (label cont)
+ (when (intset-ref reachable label)
+ (match cont
+ (($ $kargs names vars ($ $continue k src exp))
+ (unless (= (length names) (length vars))
+ (error "broken $kargs" label names vars))
+ (check-arity exp (intmap-ref conts k)))
+ (_ #t))))
+ conts)))
+
+(define (check-functions-bound-once conts kfun)
+ (let ((reachable (compute-reachable-labels conts kfun)))
+ (define (add-fun fun functions)
+ (when (intset-ref functions fun)
+ (error "function already bound" fun))
+ (intset-add functions fun))
+ (intmap-fold
+ (lambda (label cont functions)
+ (if (intset-ref reachable label)
+ (match cont
+ (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
+ (add-fun kfun functions))
+ (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
+ (fold1 add-fun kfuns functions))
+ (_ functions))
+ functions))
+ conts
+ empty-intset)))
+
+(define (verify conts)
+ (check-distinct-vars conts)
+ (check-label-partition conts 0)
+ (check-valid-var-uses conts 0)
+ (check-arities conts 0)
+ (check-functions-bound-once conts 0)
+ conts)
diff --git a/module/language/cps/with-cps.scm b/module/language/cps/with-cps.scm
new file mode 100644
index 000000000..45cb9c4fd
--- /dev/null
+++ b/module/language/cps/with-cps.scm
@@ -0,0 +1,145 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Guile's CPS language is a label->cont mapping, which seems simple
+;;; enough. However it's often cumbersome to thread around the output
+;;; CPS program when doing non-trivial transformations, or when building
+;;; a CPS program from scratch. For example, when visiting an
+;;; expression during CPS conversion, we usually already know the label
+;;; and the $kargs wrapper for the cont, and just need to know the body
+;;; of that cont. However when building the body of that possibly
+;;; nested Tree-IL expression we will also need to add conts to the
+;;; result, so really it's a process that takes an incoming program,
+;;; adds conts to that program, and returns the result program and the
+;;; result term.
+;;;
+;;; It's a bit treacherous to do in a functional style as once you start
+;;; adding to a program, you shouldn't add to previous versions of that
+;;; program. Getting that right in the context of this program seed
+;;; that is threaded through the conversion requires the use of a
+;;; pattern, with-cps.
+;;;
+;;; with-cps goes like this:
+;;;
+;;; (with-cps cps clause ... tail-clause)
+;;;
+;;; Valid clause kinds are:
+;;;
+;;; (letk LABEL CONT)
+;;; (setk LABEL CONT)
+;;; (letv VAR ...)
+;;; (let$ X (PROC ARG ...))
+;;;
+;;; letk and letv create fresh CPS labels and variable names,
+;;; respectively. Labels and vars bound by letk and letv are in scope
+;;; from their point of definition onward. letv just creates fresh
+;;; variable names for use in other parts of with-cps, while letk binds
+;;; fresh labels to values and adds them to the resulting program. The
+;;; right-hand-side of letk, CONT, is passed to build-cont, so it should
+;;; be a valid production of that language. setk is like letk but it
+;;; doesn't create a fresh label name.
+;;;
+;;; let$ delegates processing to a sub-computation. The form (PROC ARG
+;;; ...) is syntactically altered to be (PROC CPS ARG ...), where CPS is
+;;; the value of the program being built, at that point in the
+;;; left-to-right with-cps execution. That form is is expected to
+;;; evaluate to two values: the new CPS term, and the value to bind to
+;;; X. X is in scope for the following with-cps clauses. The name was
+;;; chosen because the $ is reminiscent of the $ in CPS data types.
+;;;
+;;; The result of the with-cps form is determined by the tail clause,
+;;; which may be of these kinds:
+;;;
+;;; ($ (PROC ARG ...))
+;;; (setk LABEL CONT)
+;;; EXP
+;;;
+;;; $ is like let$, but in tail position. If the tail clause is setk,
+;;; then only one value is returned, the resulting CPS program.
+;;; Otherwise EXP is any kind of expression, which should not add to the
+;;; resulting program. Ending the with-cps with EXP is equivalant to
+;;; returning (values CPS EXP).
+;;;
+;;; It's a bit of a monad, innit? Don't tell anyone though!
+;;;
+;;; Sometimes you need to just bind some constants to CPS values.
+;;; with-cps-constants is there for you. For example:
+;;;
+;;; (with-cps-constants cps ((foo 34))
+;;; (build-term ($values (foo))))
+;;;
+;;; The body of with-cps-constants is a with-cps clause, or a sequence
+;;; of such clauses. But usually you will want with-cps-constants
+;;; inside a with-cps, so it usually looks like this:
+;;;
+;;; (with-cps cps
+;;; ...
+;;; ($ (with-cps-constants ((foo 34))
+;;; (build-term ($values (foo))))))
+;;;
+;;; which is to say that the $ or the let$ adds the CPS argument for us.
+;;;
+;;; Code:
+
+(define-module (language cps with-cps)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:export (with-cps with-cps-constants))
+
+(define-syntax with-cps
+ (syntax-rules (letk setk letv let$ $)
+ ((_ (exp ...) clause ...)
+ (let ((cps (exp ...)))
+ (with-cps cps clause ...)))
+ ((_ cps (letk label cont) clause ...)
+ (let-fresh (label) ()
+ (with-cps (intmap-add! cps label (build-cont cont))
+ clause ...)))
+ ((_ cps (setk label cont))
+ (intmap-add! cps label (build-cont cont)
+ (lambda (old new) new)))
+ ((_ cps (setk label cont) clause ...)
+ (with-cps (with-cps cps (setk label cont))
+ clause ...))
+ ((_ cps (letv v ...) clause ...)
+ (let-fresh () (v ...)
+ (with-cps cps clause ...)))
+ ((_ cps (let$ var (proc arg ...)) clause ...)
+ (call-with-values (lambda () (proc cps arg ...))
+ (lambda (cps var)
+ (with-cps cps clause ...))))
+ ((_ cps ($ (proc arg ...)))
+ (proc cps arg ...))
+ ((_ cps exp)
+ (values cps exp))))
+
+(define-syntax with-cps-constants
+ (syntax-rules ()
+ ((_ cps () clause ...)
+ (with-cps cps clause ...))
+ ((_ cps ((var val) (var* val*) ...) clause ...)
+ (let ((x val))
+ (with-cps cps
+ (letv var)
+ (let$ body (with-cps-constants ((var* val*) ...)
+ clause ...))
+ (letk label ($kargs ('var) (var) ,body))
+ (build-term ($continue label #f ($const x))))))))
From b40fac1e983007e0b5f4312c1717858e966c0198 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 11:39:18 +0200
Subject: [PATCH 034/865] Factor out compute-effects/elide-type-checks from
dce.scm
* module/language/cps/type-checks.scm: New module.
* module/language/cps/dce.scm: Use new module.
* module/Makefile.am: Add new module.
---
module/Makefile.am | 1 +
module/language/cps/dce.scm | 38 +--------------
module/language/cps/type-checks.scm | 72 +++++++++++++++++++++++++++++
3 files changed, 74 insertions(+), 37 deletions(-)
create mode 100644 module/language/cps/type-checks.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index b29a4bf00..e4b088bef 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -142,6 +142,7 @@ CPS_LANG_SOURCES = \
language/cps/spec.scm \
language/cps/specialize-primcalls.scm \
language/cps/split-rec.scm \
+ language/cps/type-checks.scm \
language/cps/type-fold.scm \
language/cps/types.scm \
language/cps/utils.scm \
diff --git a/module/language/cps/dce.scm b/module/language/cps/dce.scm
index 5463f5b1e..52bd70898 100644
--- a/module/language/cps/dce.scm
+++ b/module/language/cps/dce.scm
@@ -32,48 +32,12 @@
#:use-module (language cps)
#:use-module (language cps effects-analysis)
#:use-module (language cps renumber)
- #:use-module (language cps types)
+ #:use-module (language cps type-checks)
#:use-module (language cps utils)
#:use-module (language cps intmap)
#:use-module (language cps intset)
#:export (eliminate-dead-code))
-(define (elide-type-checks conts kfun effects)
- "Elide &type-check effects from EFFECTS for the function starting at
-KFUN where we can prove that no assertion will be raised at run-time."
- (let ((types (infer-types conts kfun)))
- (define (visit-primcall effects fx label name args)
- (if (primcall-types-check? types label name args)
- (intmap-replace! effects label (logand fx (lognot &type-check)))
- effects))
- (persistent-intmap
- (intmap-fold (lambda (label types effects)
- (let ((fx (intmap-ref effects label)))
- (cond
- ((causes-all-effects? fx) effects)
- ((causes-effect? fx &type-check)
- (match (intmap-ref conts label)
- (($ $kargs _ _ exp)
- (match exp
- (($ $continue k src ($ $primcall name args))
- (visit-primcall effects fx label name args))
- (($ $continue k src
- ($ $branch _ ($primcall name args)))
- (visit-primcall effects fx label name args))
- (_ effects)))
- (_ effects)))
- (else effects))))
- types
- effects))))
-
-(define (compute-effects/elide-type-checks conts)
- (intmap-fold (lambda (label cont effects)
- (match cont
- (($ $kfun) (elide-type-checks conts label effects))
- (_ effects)))
- conts
- (compute-effects conts)))
-
(define (fold-local-conts proc conts label seed)
(match (intmap-ref conts label)
(($ $kfun src meta self tail clause)
diff --git a/module/language/cps/type-checks.scm b/module/language/cps/type-checks.scm
new file mode 100644
index 000000000..864371d28
--- /dev/null
+++ b/module/language/cps/type-checks.scm
@@ -0,0 +1,72 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; This pass kills dead expressions: code that has no side effects, and
+;;; whose value is unused. It does so by marking all live values, and
+;;; then discarding other values as dead. This happens recursively
+;;; through procedures, so it should be possible to elide dead
+;;; procedures as well.
+;;;
+;;; Code:
+
+(define-module (language cps type-checks)
+ #:use-module (ice-9 match)
+ #:use-module (language cps)
+ #:use-module (language cps effects-analysis)
+ #:use-module (language cps types)
+ #:use-module (language cps intmap)
+ #:export (elide-type-checks
+ compute-effects/elide-type-checks))
+
+(define (elide-type-checks conts kfun effects)
+ "Elide &type-check effects from EFFECTS for the function starting at
+KFUN where we can prove that no assertion will be raised at run-time."
+ (let ((types (infer-types conts kfun)))
+ (define (visit-primcall effects fx label name args)
+ (if (primcall-types-check? types label name args)
+ (intmap-replace! effects label (logand fx (lognot &type-check)))
+ effects))
+ (persistent-intmap
+ (intmap-fold (lambda (label types effects)
+ (let ((fx (intmap-ref effects label)))
+ (cond
+ ((causes-all-effects? fx) effects)
+ ((causes-effect? fx &type-check)
+ (match (intmap-ref conts label)
+ (($ $kargs _ _ exp)
+ (match exp
+ (($ $continue k src ($ $primcall name args))
+ (visit-primcall effects fx label name args))
+ (($ $continue k src
+ ($ $branch _ ($primcall name args)))
+ (visit-primcall effects fx label name args))
+ (_ effects)))
+ (_ effects)))
+ (else effects))))
+ types
+ effects))))
+
+(define (compute-effects/elide-type-checks conts)
+ (intmap-fold (lambda (label cont effects)
+ (match cont
+ (($ $kfun) (elide-type-checks conts label effects))
+ (_ effects)))
+ conts
+ (compute-effects conts)))
From bebc70c8b1f584c9f1e360ffc38094af4a4cee49 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 11:40:00 +0200
Subject: [PATCH 035/865] Move solve-flow-equations to utils
* module/language/cps/slot-allocation.scm (compute-lazy-vars):
(compute-live-variables): Adapt to solve-flow-equations interface
change.
* module/language/cps/utils.scm (solve-flow-equations): Move here. Use
an init value instead of an init map.
---
module/language/cps/slot-allocation.scm | 60 +++----------------------
module/language/cps/utils.scm | 46 ++++++++++++++++++-
2 files changed, 51 insertions(+), 55 deletions(-)
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
index 74e71c487..6039214c8 100644
--- a/module/language/cps/slot-allocation.scm
+++ b/module/language/cps/slot-allocation.scm
@@ -128,48 +128,6 @@
(define (lookup-nlocals k allocation)
(intmap-ref (allocation-frame-sizes allocation) k))
-(define (intset-pop set)
- (match (intset-next set)
- (#f (values set #f))
- (i (values (intset-remove set i) i))))
-
-(define (solve-flow-equations succs in out kill gen subtract add meet)
- "Find a fixed point for flow equations for SUCCS, where IN and OUT are
-the initial conditions as intmaps with one key for every node in SUCCS.
-KILL and GEN are intmaps indicating the state that is killed or defined
-at every node, and SUBTRACT, ADD, and MEET operates on that state."
- (define (visit label in out)
- (let* ((in-1 (intmap-ref in label))
- (kill-1 (intmap-ref kill label))
- (gen-1 (intmap-ref gen label))
- (out-1 (intmap-ref out label))
- (out-1* (add (subtract in-1 kill-1) gen-1)))
- (if (eq? out-1 out-1*)
- (values empty-intset in out)
- (let ((out (intmap-replace! out label out-1*)))
- (call-with-values
- (lambda ()
- (intset-fold (lambda (succ in changed)
- (let* ((in-1 (intmap-ref in succ))
- (in-1* (meet in-1 out-1*)))
- (if (eq? in-1 in-1*)
- (values in changed)
- (values (intmap-replace! in succ in-1*)
- (intset-add changed succ)))))
- (intmap-ref succs label) in empty-intset))
- (lambda (in changed)
- (values changed in out)))))))
-
- (let run ((worklist (intmap-keys succs)) (in in) (out out))
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist popped)
- (if popped
- (call-with-values (lambda () (visit popped in out))
- (lambda (changed in out)
- (run (intset-union worklist changed) in out)))
- (values (persistent-intmap in)
- (persistent-intmap out)))))))
-
(define-syntax-rule (persistent-intmap2 exp)
(call-with-values (lambda () exp)
(lambda (a b)
@@ -321,14 +279,11 @@ the definitions that are live before and after LABEL, as intsets."
(old->new (compute-reverse-control-flow-order preds)))
(call-with-values
(lambda ()
- (let ((init (rename-keys
- (intmap-map (lambda (k v) empty-intset) preds)
- old->new)))
- (solve-flow-equations (rename-graph preds old->new)
- init init
- (rename-keys defs old->new)
- (rename-keys uses old->new)
- intset-subtract intset-union intset-union)))
+ (solve-flow-equations (rename-graph preds old->new)
+ empty-intset
+ (rename-keys defs old->new)
+ (rename-keys uses old->new)
+ intset-subtract intset-union intset-union))
(lambda (in out)
;; As a reverse control-flow problem, the values flowing into a
;; node are actually the live values after the node executes.
@@ -448,12 +403,9 @@ is an active call."
(call-with-values
(lambda ()
(let ((succs (rename-graph preds old->new))
- (in (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
- (out (rename-keys (intmap-map (lambda (k v) #f) preds) old->new))
- ;(out (rename-keys gens old->new))
(kills (rename-keys kills old->new))
(gens (rename-keys gens old->new)))
- (solve-flow-equations succs in out kills gens subtract add meet)))
+ (solve-flow-equations succs #f kills gens subtract add meet)))
(lambda (in out)
;; A variable is lazy if its uses reach its definition.
(intmap-fold (lambda (label out lazy)
diff --git a/module/language/cps/utils.scm b/module/language/cps/utils.scm
index fa4673c4e..9f95e0171 100644
--- a/module/language/cps/utils.scm
+++ b/module/language/cps/utils.scm
@@ -57,6 +57,7 @@
compute-sorted-strongly-connected-components
compute-idoms
compute-dom-edges
+ solve-flow-equations
))
(define label-counter (make-parameter #f))
@@ -233,7 +234,7 @@ disjoint, an error will be signalled."
(visit-cont k labels))
(_ labels)))))))))))
-(define (compute-reachable-functions conts kfun)
+(define* (compute-reachable-functions conts #:optional (kfun 0))
"Compute a mapping LABEL->LABEL..., where each key is a reachable
$kfun and each associated value is the body of the function, as an
intset."
@@ -475,3 +476,46 @@ connected components in sorted order."
(else (intmap-add! doms idom label snoc)))))
idoms
empty-intmap)))
+
+(define (intset-pop set)
+ (match (intset-next set)
+ (#f (values set #f))
+ (i (values (intset-remove set i) i))))
+
+(define (solve-flow-equations succs init kill gen subtract add meet)
+ "Find a fixed point for flow equations for SUCCS, where INIT is the
+initial state at each node in SUCCS. KILL and GEN are intmaps
+indicating the state that is killed or defined at every node, and
+SUBTRACT, ADD, and MEET operates on that state."
+ (define (visit label in out)
+ (let* ((in-1 (intmap-ref in label))
+ (kill-1 (intmap-ref kill label))
+ (gen-1 (intmap-ref gen label))
+ (out-1 (intmap-ref out label))
+ (out-1* (add (subtract in-1 kill-1) gen-1)))
+ (if (eq? out-1 out-1*)
+ (values empty-intset in out)
+ (let ((out (intmap-replace! out label out-1*)))
+ (call-with-values
+ (lambda ()
+ (intset-fold (lambda (succ in changed)
+ (let* ((in-1 (intmap-ref in succ))
+ (in-1* (meet in-1 out-1*)))
+ (if (eq? in-1 in-1*)
+ (values in changed)
+ (values (intmap-replace! in succ in-1*)
+ (intset-add changed succ)))))
+ (intmap-ref succs label) in empty-intset))
+ (lambda (in changed)
+ (values changed in out)))))))
+
+ (let ((init (intmap-map (lambda (k v) init) succs)))
+ (let run ((worklist (intmap-keys succs)) (in init) (out init))
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist popped)
+ (if popped
+ (call-with-values (lambda () (visit popped in out))
+ (lambda (changed in out)
+ (run (intset-union worklist changed) in out)))
+ (values (persistent-intmap in)
+ (persistent-intmap out))))))))
From ec9554d138a222d9ba59ecf7ee0b68d5c7ec1dfd Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 11:42:38 +0200
Subject: [PATCH 036/865] Loop-invariant code motion
* module/language/cps/licm.scm: New pass.
* module/language/cps/optimize.scm: Wire up new pass.
* module/Makefile.am: Add new file.
---
module/Makefile.am | 1 +
module/language/cps/licm.scm | 308 +++++++++++++++++++++++++++++++
module/language/cps/optimize.scm | 2 +
3 files changed, 311 insertions(+)
create mode 100644 module/language/cps/licm.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index e4b088bef..67671daef 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -130,6 +130,7 @@ CPS_LANG_SOURCES = \
language/cps/dce.scm \
language/cps/effects-analysis.scm \
language/cps/elide-values.scm \
+ language/cps/licm.scm \
language/cps/primitives.scm \
language/cps/prune-bailouts.scm \
language/cps/prune-top-level-scopes.scm \
diff --git a/module/language/cps/licm.scm b/module/language/cps/licm.scm
new file mode 100644
index 000000000..3b343a66b
--- /dev/null
+++ b/module/language/cps/licm.scm
@@ -0,0 +1,308 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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)
+ (($ $primcall name 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)))
diff --git a/module/language/cps/optimize.scm b/module/language/cps/optimize.scm
index 83a3f2dfe..c7545cc42 100644
--- a/module/language/cps/optimize.scm
+++ b/module/language/cps/optimize.scm
@@ -29,6 +29,7 @@
#:use-module (language cps cse)
#:use-module (language cps dce)
#:use-module (language cps elide-values)
+ #:use-module (language cps licm)
#:use-module (language cps prune-top-level-scopes)
#:use-module (language cps prune-bailouts)
#:use-module (language cps self-references)
@@ -95,6 +96,7 @@
(specialize-primcalls #:specialize-primcalls? #t)
(elide-values #:elide-values? #t)
(prune-bailouts #:prune-bailouts? #t)
+ (hoist-loop-invariant-code #:licm? #t)
(eliminate-common-subexpressions #:cse? #t)
(type-fold #:type-fold? #t)
(resolve-self-references #:resolve-self-references? #t)
From bcfa9fe70ec26424ee5c229fbc4bc748f95ce953 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 12:21:00 +0200
Subject: [PATCH 037/865] Small expression-effects tweak
* module/language/cps/effects-analysis.scm (expression-effects):
Closures with zero free vars don't allocate.
---
module/language/cps/effects-analysis.scm | 2 ++
1 file changed, 2 insertions(+)
diff --git a/module/language/cps/effects-analysis.scm b/module/language/cps/effects-analysis.scm
index 874eb7829..af1a5292e 100644
--- a/module/language/cps/effects-analysis.scm
+++ b/module/language/cps/effects-analysis.scm
@@ -438,6 +438,8 @@ is or might be a read or a write to the same location as A."
(match exp
((or ($ $const) ($ $prim) ($ $values))
&no-effects)
+ (($ $closure _ 0)
+ &no-effects)
((or ($ $fun) ($ $rec) ($ $closure))
(&allocate &unknown-memory-kinds))
(($ $prompt)
From bf6930b3f60a371039542570d6149ea04d3612ea Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 15:55:46 +0200
Subject: [PATCH 038/865] Eliminate trampoline gotos when possible in
compile-bytecode
* module/language/cps/compile-bytecode.scm (compile-function): Eliminate
trampoline jumps for conditional branches that don't shuffle.
---
module/language/cps/compile-bytecode.scm | 124 +++++++++++++----------
1 file changed, 71 insertions(+), 53 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index 61f1e0781..a57074380 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -242,6 +242,18 @@
(($ $primcall 'unwind ())
(emit-unwind asm))))
+ (define (forward-label label seen)
+ (if (memv label seen)
+ label
+ (match (intmap-ref cps label)
+ (($ $kargs _ _ ($ $continue k _ ($ $values)))
+ (match (lookup-parallel-moves label allocation)
+ (() (match (intmap-ref cps k)
+ (($ $ktail) label)
+ (_ (forward-label k (cons label seen)))))
+ (_ label)))
+ (cont label))))
+
(define (compile-values label exp syms)
(match exp
(($ $values args)
@@ -250,58 +262,62 @@
(lookup-parallel-moves label allocation)))))
(define (compile-test label exp kt kf next-label)
- (define (unary op sym)
- (cond
- ((eq? kt next-label)
- (op asm (slot sym) #t kf))
- (else
- (op asm (slot sym) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (define (binary op a b)
- (cond
- ((eq? kt next-label)
- (op asm (slot a) (slot b) #t kf))
- (else
- (op asm (slot a) (slot b) #f kt)
- (unless (eq? kf next-label)
- (emit-br asm kf)))))
- (match exp
- (($ $values (sym))
- (call-with-values (lambda ()
- (lookup-maybe-constant-value sym allocation))
- (lambda (has-const? val)
- (if has-const?
- (if val
- (unless (eq? kt next-label)
- (emit-br asm kt))
- (unless (eq? kf next-label)
- (emit-br asm kf)))
- (unary emit-br-if-true sym)))))
- (($ $primcall 'null? (a)) (unary emit-br-if-null a))
- (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
- (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
- (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
- (($ $primcall 'char? (a)) (unary emit-br-if-char a))
- (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
- (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
- (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
- (($ $primcall 'string? (a)) (unary emit-br-if-string a))
- (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
- (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
- (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
- ;; Add more TC7 tests here. Keep in sync with
- ;; *branching-primcall-arities* in (language cps primitives) and
- ;; the set of macro-instructions in assembly.scm.
- (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
- (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
- (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
- (($ $primcall '< (a b)) (binary emit-br-if-< a b))
- (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
- (($ $primcall '= (a b)) (binary emit-br-if-= a b))
- (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
- (($ $primcall '> (a b)) (binary emit-br-if-< b a))
- (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
+ (let* ((kt (forward-label kt '()))
+ (kf (forward-label kf '())))
+ (define (prefer-true?)
+ (if (< (max kt kf) label)
+ ;; Two backwards branches. Prefer
+ ;; the nearest.
+ (> kt kf)
+ ;; Otherwise prefer a backwards
+ ;; branch or a near jump.
+ (< kt kf)))
+ (define (unary op sym)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot sym) #t kf))
+ ((eq? kf next-label)
+ (op asm (slot sym) #f kt))
+ (else
+ (let ((invert? (not (prefer-true?))))
+ (op asm (slot sym) invert? (if invert? kf kt))
+ (emit-br asm (if invert? kt kf))))))
+ (define (binary op a b)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot a) (slot b) #t kf))
+ ((eq? kf next-label)
+ (op asm (slot a) (slot b) #f kt))
+ (else
+ (let ((invert? (not (prefer-true?))))
+ (op asm (slot a) (slot b) invert? (if invert? kf kt))
+ (emit-br asm (if invert? kt kf))))))
+ (match exp
+ (($ $values (sym)) (unary emit-br-if-true sym))
+ (($ $primcall 'null? (a)) (unary emit-br-if-null a))
+ (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
+ (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
+ (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
+ (($ $primcall 'char? (a)) (unary emit-br-if-char a))
+ (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
+ (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
+ (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
+ (($ $primcall 'string? (a)) (unary emit-br-if-string a))
+ (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
+ (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
+ (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
+ ;; Add more TC7 tests here. Keep in sync with
+ ;; *branching-primcall-arities* in (language cps primitives) and
+ ;; the set of macro-instructions in assembly.scm.
+ (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
+ (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
+ (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
+ (($ $primcall '< (a b)) (binary emit-br-if-< a b))
+ (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
+ (($ $primcall '= (a b)) (binary emit-br-if-= a b))
+ (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
+ (($ $primcall '> (a b)) (binary emit-br-if-< b a))
+ (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b)))))
(define (compile-trunc label k exp nreq rest-var)
(define (do-call proc args emit-call)
@@ -346,7 +362,9 @@
(define (compile-expression label k exp)
(let* ((fallthrough? (= k (1+ label))))
(define (maybe-emit-jump)
- (unless fallthrough?
+ (unless (or fallthrough?
+ (= (forward-label k '())
+ (forward-label (1+ label) '())))
(emit-br asm k)))
(match (intmap-ref cps k)
(($ $ktail)
From ee85e2969f623278e095c5facda4430b664a04aa Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 24 Jul 2015 16:50:19 +0200
Subject: [PATCH 039/865] Rotate comparisons down to loop back-edges
* module/language/cps/rotate-loops.scm: New pass.
* module/Makefile.am:
* module/language/cps/optimize.scm: Wire up the new pass.
---
module/Makefile.am | 1 +
module/language/cps/optimize.scm | 2 +
module/language/cps/rotate-loops.scm | 217 +++++++++++++++++++++++++++
3 files changed, 220 insertions(+)
create mode 100644 module/language/cps/rotate-loops.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 67671daef..a946da36d 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -136,6 +136,7 @@ CPS_LANG_SOURCES = \
language/cps/prune-top-level-scopes.scm \
language/cps/reify-primitives.scm \
language/cps/renumber.scm \
+ language/cps/rotate-loops.scm \
language/cps/optimize.scm \
language/cps/simplify.scm \
language/cps/self-references.scm \
diff --git a/module/language/cps/optimize.scm b/module/language/cps/optimize.scm
index c7545cc42..7721d6385 100644
--- a/module/language/cps/optimize.scm
+++ b/module/language/cps/optimize.scm
@@ -32,6 +32,7 @@
#:use-module (language cps licm)
#:use-module (language cps prune-top-level-scopes)
#:use-module (language cps prune-bailouts)
+ #:use-module (language cps rotate-loops)
#:use-module (language cps self-references)
#:use-module (language cps simplify)
#:use-module (language cps specialize-primcalls)
@@ -105,4 +106,5 @@
(define-optimizer optimize-first-order-cps
(eliminate-dead-code #:eliminate-dead-code? #t)
+ (rotate-loops #:rotate-loops? #t)
(simplify #:simplify? #t))
diff --git a/module/language/cps/rotate-loops.scm b/module/language/cps/rotate-loops.scm
new file mode 100644
index 000000000..19ecf444c
--- /dev/null
+++ b/module/language/cps/rotate-loops.scm
@@ -0,0 +1,217 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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:
+;;;
+;;; Rotate loops so that they end with conditional jumps, if possible.
+;;; The result goes from:
+;;;
+;;; loop:
+;;; if x < 5 goto done;
+;;; x = x + 1;
+;;; goto loop;
+;;; done:
+;;;
+;;; if x < 5 goto done;
+;;; loop:
+;;; x = x + 1;
+;;; if x < 5 goto done;
+;;; done:
+;;;
+;;; It's more code but there are fewer instructions in the body. Note
+;;; that this transformation isn't guaranteed to produce a loop that
+;;; ends in a conditional jump, because usually your loop has some state
+;;; that it's shuffling around and for now that shuffle is reified with
+;;; the test, not the loop header. Alack.
+;;;
+;;; Implementation-wise, things are complicated by values flowing out of
+;;; the loop. We actually perform this transformation only on loops
+;;; that have a single exit continuation, so that we define values
+;;; flowing out in one place. We rename the loop variables in two
+;;; places internally: one for the peeled comparison, and another for
+;;; the body. The loop variables' original names are then bound in a
+;;; join continuation for use by successor code.
+;;;
+;;; Code:
+
+(define-module (language cps rotate-loops)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (filter-map))
+ #:use-module (srfi srfi-9)
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (rotate-loops))
+
+(define-record-type $loop
+ (make-loop entry exits body)
+ loop?
+ (entry loop-entry)
+ (exits loop-exits)
+ (body loop-body))
+
+(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 (rotate-loop cps entry-label body-labels succs preds back-edges)
+ (match (intmap-ref cps entry-label)
+ ((and entry-cont
+ ($ $kargs entry-names entry-vars
+ ($ $continue entry-kf entry-src ($ $branch entry-kt entry-exp))))
+ (let* ((exit-if-true? (intset-ref body-labels entry-kf))
+ (exit (if exit-if-true? entry-kt entry-kf))
+ (new-entry-label (if exit-if-true? entry-kf entry-kt))
+ (join-label (fresh-label))
+ (join-cont (build-cont
+ ($kargs entry-names entry-vars
+ ($continue exit entry-src ($values ())))))
+ (cps (intmap-add! cps join-label join-cont)))
+ (define (make-fresh-vars)
+ (map (lambda (_) (fresh-var)) entry-vars))
+ (define (make-trampoline k src values)
+ (build-cont ($kargs () () ($continue k src ($values values)))))
+ (define (replace-exit k trampoline)
+ (if (eqv? k exit) trampoline k))
+ (define (rename-exp exp vars)
+ (define (rename-var var)
+ (match (list-index entry-vars var)
+ (#f var)
+ (idx (list-ref vars idx))))
+ (rewrite-exp exp
+ ((or ($ $const) ($ $prim) ($ $closure)) ,exp)
+ (($ $values args)
+ ($values ,(map rename-var args)))
+ (($ $call proc args)
+ ($call (rename-var proc) ,(map rename-var args)))
+ (($ $callk k proc args)
+ ($callk k (rename-var proc) ,(map rename-var args)))
+ (($ $branch kt ($ $values (arg)))
+ ($branch kt ($values ((rename-var arg)))))
+ (($ $branch kt ($ $primcall name args))
+ ($branch kt ($primcall name ,(map rename-var args))))
+ (($ $primcall name args)
+ ($primcall name ,(map rename-var args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (rename-var tag) handler))))
+ (define (attach-trampoline label src names vars args)
+ (let* ((trampoline-out-label (fresh-label))
+ (trampoline-out-cont
+ (make-trampoline join-label src args))
+ (trampoline-in-label (fresh-label))
+ (trampoline-in-cont
+ (make-trampoline new-entry-label src args))
+ (kf (if exit-if-true? trampoline-in-label trampoline-out-label))
+ (kt (if exit-if-true? trampoline-out-label trampoline-in-label))
+ (cont (build-cont
+ ($kargs names vars
+ ($continue kf entry-src
+ ($branch kt ,(rename-exp entry-exp args))))))
+ (cps (intmap-replace! cps label cont))
+ (cps (intmap-add! cps trampoline-in-label trampoline-in-cont)))
+ (intmap-add! cps trampoline-out-label trampoline-out-cont)))
+ ;; Rewrite the targets of the entry branch to go to
+ ;; trampolines. One will pass values out of the loop, and
+ ;; one will pass values into the loop.
+ (let* ((pre-header-vars (make-fresh-vars))
+ (body-vars (make-fresh-vars))
+ (cps (attach-trampoline entry-label entry-src
+ entry-names pre-header-vars
+ pre-header-vars))
+ (new-entry-cont (build-cont
+ ($kargs entry-names body-vars
+ ,(match (intmap-ref cps new-entry-label)
+ (($ $kargs () () term) term)))))
+ (cps (intmap-replace! cps new-entry-label new-entry-cont)))
+ (intset-fold
+ (lambda (label cps)
+ (if (intset-ref back-edges label)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue _ src exp))
+ (match (rename-exp exp body-vars)
+ (($ $values args)
+ (attach-trampoline label src names vars args))
+ (exp
+ (let* ((args (make-fresh-vars))
+ (bind-label (fresh-label))
+ (edge* (build-cont
+ ($kargs names vars
+ ($continue bind-label src ,exp))))
+ (cps (intmap-replace! cps label edge*))
+ ;; attach-trampoline uses intmap-replace!.
+ (cps (intmap-add! cps bind-label #f)))
+ (attach-trampoline bind-label src
+ entry-names args args))))))
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (let ((cont (build-cont
+ ($kargs names vars
+ ($continue k src
+ ,(rename-exp exp body-vars))))))
+ (intmap-replace! cps label cont)))
+ (($ $kreceive) cps))))
+ (intset-remove body-labels entry-label)
+ cps))))))
+
+(define (rotate-loops-in-function kfun body cps)
+ (define (can-rotate? edges)
+ (intset-fold (lambda (label rotate?)
+ (match (intmap-ref cps label)
+ (($ $kreceive) #f)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $branch) #f)
+ (_ rotate?)))))
+ edges #t))
+ (let* ((succs (compute-successors cps kfun))
+ (preds (invert-graph succs)))
+ (intmap-fold
+ (lambda (id scc cps)
+ (cond
+ ((trivial-intset scc) cps)
+ ((find-entry scc preds)
+ => (lambda (entry)
+ (let ((back-edges (intset-intersect scc
+ (intmap-ref preds entry))))
+ (if (and (can-rotate? back-edges)
+ (eqv? (trivial-intset (find-exits scc succs)) entry))
+ ;; Loop header is the only exit. It must be a
+ ;; conditional branch and only one successor is an
+ ;; exit. The values flowing out of the loop are the
+ ;; loop variables.
+ (rotate-loop cps entry scc succs preds back-edges)
+ cps))))
+ (else cps)))
+ (compute-strongly-connected-components succs kfun)
+ cps)))
+
+(define (rotate-loops cps)
+ (persistent-intmap
+ (with-fresh-name-state cps
+ (intmap-fold rotate-loops-in-function
+ (compute-reachable-functions cps)
+ cps))))
From e54fbff185786886b56f0438040c2a1d54363c6a Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sat, 25 Jul 2015 11:03:59 +0200
Subject: [PATCH 040/865] Loop inversion with multiple exits
* module/language/cps/rotate-loops.scm (rotate-loop): Instead of
restricting rotation to loops with just one exit node, restrict to
loops with just one exit successor.
---
module/language/cps/rotate-loops.scm | 81 ++++++++++++++++++----------
1 file changed, 54 insertions(+), 27 deletions(-)
diff --git a/module/language/cps/rotate-loops.scm b/module/language/cps/rotate-loops.scm
index 19ecf444c..c6b68bb32 100644
--- a/module/language/cps/rotate-loops.scm
+++ b/module/language/cps/rotate-loops.scm
@@ -66,6 +66,12 @@
(exits loop-exits)
(body loop-body))
+(define (loop-successors scc succs)
+ (intset-subtract (intset-fold (lambda (label exits)
+ (intset-union exits (intmap-ref succs label)))
+ scc empty-intset)
+ scc))
+
(define (find-exits scc succs)
(intset-fold (lambda (label exits)
(if (eq? empty-intset
@@ -84,6 +90,7 @@
($ $kargs entry-names entry-vars
($ $continue entry-kf entry-src ($ $branch entry-kt entry-exp))))
(let* ((exit-if-true? (intset-ref body-labels entry-kf))
+ (loop-exits (find-exits body-labels succs))
(exit (if exit-if-true? entry-kt entry-kf))
(new-entry-label (if exit-if-true? entry-kf entry-kt))
(join-label (fresh-label))
@@ -149,31 +156,48 @@
(cps (intmap-replace! cps new-entry-label new-entry-cont)))
(intset-fold
(lambda (label cps)
- (if (intset-ref back-edges label)
- (match (intmap-ref cps label)
- (($ $kargs names vars ($ $continue _ src exp))
- (match (rename-exp exp body-vars)
- (($ $values args)
- (attach-trampoline label src names vars args))
- (exp
- (let* ((args (make-fresh-vars))
- (bind-label (fresh-label))
- (edge* (build-cont
- ($kargs names vars
- ($continue bind-label src ,exp))))
- (cps (intmap-replace! cps label edge*))
- ;; attach-trampoline uses intmap-replace!.
- (cps (intmap-add! cps bind-label #f)))
- (attach-trampoline bind-label src
- entry-names args args))))))
- (match (intmap-ref cps label)
- (($ $kargs names vars ($ $continue k src exp))
- (let ((cont (build-cont
- ($kargs names vars
- ($continue k src
- ,(rename-exp exp body-vars))))))
- (intmap-replace! cps label cont)))
- (($ $kreceive) cps))))
+ (cond
+ ((intset-ref back-edges label)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue _ src exp))
+ (match (rename-exp exp body-vars)
+ (($ $values args)
+ (attach-trampoline label src names vars args))
+ (exp
+ (let* ((args (make-fresh-vars))
+ (bind-label (fresh-label))
+ (edge* (build-cont
+ ($kargs names vars
+ ($continue bind-label src ,exp))))
+ (cps (intmap-replace! cps label edge*))
+ ;; attach-trampoline uses intmap-replace!.
+ (cps (intmap-add! cps bind-label #f)))
+ (attach-trampoline bind-label src
+ entry-names args args)))))))
+ ((intset-ref loop-exits label)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars
+ ($ $continue kf src ($ $branch kt exp)))
+ (let* ((trampoline-out-label (fresh-label))
+ (trampoline-out-cont
+ (make-trampoline join-label src body-vars))
+ (kf (if (eqv? kf exit) trampoline-out-label kf))
+ (kt (if (eqv? kt exit) trampoline-out-label kt))
+ (cont (build-cont
+ ($kargs names vars
+ ($continue kf src
+ ($branch kt ,(rename-exp exp body-vars))))))
+ (cps (intmap-replace! cps label cont)))
+ (intmap-add! cps trampoline-out-label trampoline-out-cont)))))
+ (else
+ (match (intmap-ref cps label)
+ (($ $kargs names vars ($ $continue k src exp))
+ (let ((cont (build-cont
+ ($kargs names vars
+ ($continue k src
+ ,(rename-exp exp body-vars))))))
+ (intmap-replace! cps label cont)))
+ (($ $kreceive) cps)))))
(intset-remove body-labels entry-label)
cps))))))
@@ -198,8 +222,11 @@
(let ((back-edges (intset-intersect scc
(intmap-ref preds entry))))
(if (and (can-rotate? back-edges)
- (eqv? (trivial-intset (find-exits scc succs)) entry))
- ;; Loop header is the only exit. It must be a
+ (trivial-intset
+ (intset-subtract (intmap-ref succs entry) scc))
+ (trivial-intset (loop-successors scc succs)))
+ ;; Loop header is an exit, and there is only one
+ ;; exit continuation. Loop header must then be a
;; conditional branch and only one successor is an
;; exit. The values flowing out of the loop are the
;; loop variables.
From ce2888701c02870aeaeba53675e2cc132c9d10dc Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 27 Jul 2015 12:59:09 +0200
Subject: [PATCH 041/865] Simplify rotate-loops.scm
* module/language/cps/rotate-loops.scm: Clean up unused code.
---
module/language/cps/rotate-loops.scm | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/module/language/cps/rotate-loops.scm b/module/language/cps/rotate-loops.scm
index c6b68bb32..0fab94f1d 100644
--- a/module/language/cps/rotate-loops.scm
+++ b/module/language/cps/rotate-loops.scm
@@ -51,21 +51,12 @@
(define-module (language cps rotate-loops)
#:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (filter-map))
- #:use-module (srfi srfi-9)
#:use-module (language cps)
#:use-module (language cps utils)
#:use-module (language cps intmap)
#:use-module (language cps intset)
#:export (rotate-loops))
-(define-record-type $loop
- (make-loop entry exits body)
- loop?
- (entry loop-entry)
- (exits loop-exits)
- (body loop-body))
-
(define (loop-successors scc succs)
(intset-subtract (intset-fold (lambda (label exits)
(intset-union exits (intmap-ref succs label)))
From 4792577ab8c5c6264a48cc8d6592ca7c1103c2c7 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 27 Jul 2015 13:25:38 +0200
Subject: [PATCH 042/865] solve-flow-equations tweak
* module/language/cps/utils.scm (solve-flow-equations): Revert to take
separate in and out maps. Take an optional initial worklist.
* module/language/cps/slot-allocation.scm: Adapt to solve-flow-equations
change.
---
module/language/cps/slot-allocation.scm | 16 +++++++++++++---
module/language/cps/utils.scm | 22 +++++++++++-----------
2 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
index 6039214c8..8be36e716 100644
--- a/module/language/cps/slot-allocation.scm
+++ b/module/language/cps/slot-allocation.scm
@@ -276,11 +276,15 @@ body continuation in the prompt."
the definitions that are live before and after LABEL, as intsets."
(let* ((succs (add-prompt-control-flow-edges cps (compute-successors cps)))
(preds (invert-graph succs))
- (old->new (compute-reverse-control-flow-order preds)))
+ (old->new (compute-reverse-control-flow-order preds))
+ (init (persistent-intmap (intmap-fold
+ (lambda (old new init)
+ (intmap-add! init new empty-intset))
+ old->new empty-intmap))))
(call-with-values
(lambda ()
(solve-flow-equations (rename-graph preds old->new)
- empty-intset
+ init init
(rename-keys defs old->new)
(rename-keys uses old->new)
intset-subtract intset-union intset-union))
@@ -403,9 +407,15 @@ is an active call."
(call-with-values
(lambda ()
(let ((succs (rename-graph preds old->new))
+ (init (persistent-intmap
+ (intmap-fold
+ (lambda (old new in)
+ (intmap-add! in new #f))
+ old->new empty-intmap)))
(kills (rename-keys kills old->new))
(gens (rename-keys gens old->new)))
- (solve-flow-equations succs #f kills gens subtract add meet)))
+ (solve-flow-equations succs init init kills gens
+ subtract add meet)))
(lambda (in out)
;; A variable is lazy if its uses reach its definition.
(intmap-fold (lambda (label out lazy)
diff --git a/module/language/cps/utils.scm b/module/language/cps/utils.scm
index 9f95e0171..fcbda9e76 100644
--- a/module/language/cps/utils.scm
+++ b/module/language/cps/utils.scm
@@ -482,7 +482,8 @@ connected components in sorted order."
(#f (values set #f))
(i (values (intset-remove set i) i))))
-(define (solve-flow-equations succs init kill gen subtract add meet)
+(define* (solve-flow-equations succs in out kill gen subtract add meet
+ #:optional (worklist (intmap-keys succs)))
"Find a fixed point for flow equations for SUCCS, where INIT is the
initial state at each node in SUCCS. KILL and GEN are intmaps
indicating the state that is killed or defined at every node, and
@@ -509,13 +510,12 @@ SUBTRACT, ADD, and MEET operates on that state."
(lambda (in changed)
(values changed in out)))))))
- (let ((init (intmap-map (lambda (k v) init) succs)))
- (let run ((worklist (intmap-keys succs)) (in init) (out init))
- (call-with-values (lambda () (intset-pop worklist))
- (lambda (worklist popped)
- (if popped
- (call-with-values (lambda () (visit popped in out))
- (lambda (changed in out)
- (run (intset-union worklist changed) in out)))
- (values (persistent-intmap in)
- (persistent-intmap out))))))))
+ (let run ((worklist worklist) (in in) (out out))
+ (call-with-values (lambda () (intset-pop worklist))
+ (lambda (worklist popped)
+ (if popped
+ (call-with-values (lambda () (visit popped in out))
+ (lambda (changed in out)
+ (run (intset-union worklist changed) in out)))
+ (values (persistent-intmap in)
+ (persistent-intmap out)))))))
From 3b60e79879c91bc5083f7a38db5a38ce4bfb4da8 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 27 Jul 2015 13:45:23 +0200
Subject: [PATCH 043/865] Loop peeling
* module/language/cps/peel-loops.scm: New pass. Only enabled if the
loop has one successor.
* module/language/cps/optimize.scm: Peel instead of doing LICM on
higher-order CPS, then LICM on first-order CPS.
* module/Makefile.am: Wire up new pass.
---
module/Makefile.am | 1 +
module/language/cps/optimize.scm | 10 +-
module/language/cps/peel-loops.scm | 287 +++++++++++++++++++++++++++++
3 files changed, 292 insertions(+), 6 deletions(-)
create mode 100644 module/language/cps/peel-loops.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index a946da36d..3dc6117d3 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -131,6 +131,7 @@ CPS_LANG_SOURCES = \
language/cps/effects-analysis.scm \
language/cps/elide-values.scm \
language/cps/licm.scm \
+ language/cps/peel-loops.scm \
language/cps/primitives.scm \
language/cps/prune-bailouts.scm \
language/cps/prune-top-level-scopes.scm \
diff --git a/module/language/cps/optimize.scm b/module/language/cps/optimize.scm
index 7721d6385..c6576fc4a 100644
--- a/module/language/cps/optimize.scm
+++ b/module/language/cps/optimize.scm
@@ -30,6 +30,7 @@
#:use-module (language cps dce)
#:use-module (language cps elide-values)
#:use-module (language cps licm)
+ #:use-module (language cps peel-loops)
#:use-module (language cps prune-top-level-scopes)
#:use-module (language cps prune-bailouts)
#:use-module (language cps rotate-loops)
@@ -82,11 +83,6 @@
;; * Abort contification: turning abort primcalls into continuation
;; calls, and eliding prompts if possible.
;;
-;; * Loop peeling. Unrolls the first round through a loop if the
-;; loop has effects that CSE can work on. Requires effects
-;; analysis. When run before CSE, loop peeling is the equivalent
-;; of loop-invariant code motion (LICM).
-;;
(define-optimizer optimize-higher-order-cps
(split-rec #:split-rec? #t)
(eliminate-dead-code #:eliminate-dead-code? #t)
@@ -97,7 +93,7 @@
(specialize-primcalls #:specialize-primcalls? #t)
(elide-values #:elide-values? #t)
(prune-bailouts #:prune-bailouts? #t)
- (hoist-loop-invariant-code #:licm? #t)
+ (peel-loops #:peel-loops? #t)
(eliminate-common-subexpressions #:cse? #t)
(type-fold #:type-fold? #t)
(resolve-self-references #:resolve-self-references? #t)
@@ -105,6 +101,8 @@
(simplify #:simplify? #t))
(define-optimizer optimize-first-order-cps
+ (hoist-loop-invariant-code #:licm? #t)
+ ;; FIXME: CSE here to eliminate duplicate free-ref terms.
(eliminate-dead-code #:eliminate-dead-code? #t)
(rotate-loops #:rotate-loops? #t)
(simplify #:simplify? #t))
diff --git a/module/language/cps/peel-loops.scm b/module/language/cps/peel-loops.scm
new file mode 100644
index 000000000..a1b04a45b
--- /dev/null
+++ b/module/language/cps/peel-loops.scm
@@ -0,0 +1,287 @@
+;;; Continuation-passing style (CPS) intermediate language (IL)
+
+;; Copyright (C) 2013, 2014, 2015 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 peeling "peels off" one iteration of a loop. When followed by
+;;; common subexpression elimination, it has the effect of moving terms
+;;; to the first peeled iteration, leaving the loop body with fewer
+;;; terms.
+;;;
+;;; Loop peeling is complementary to loop-invariant code motion (LICM).
+;;; LICM will hoist invariant terms that have no side effects, like
+;;; $const, even if they are in branches that are not always taken.
+;;; However LICM won't hoist expressions that might have side effects if
+;;; it can't prove that they are reachable on every iteration. Peeling
+;;; on the other hand arranges for the body to be dominated by one loop
+;;; iteration, so any effect that is reachable on one full iteration can
+;;; be hoisted and eliminated, which is a big boon when we consider
+;;; &type-check effects. For example:
+;;;
+;;; x = cached-toplevel-box map
+;;; y = box-ref x
+;;; z = cached-toplevel-box foo
+;;; w = box-ref z
+;;; ...
+;;;
+;;; In this example, LICM could hoist X, possibly Y as well if it can
+;;; prove that the body doesn't write to variables, but it won't hoist
+;;; Z. In contrast, peeling + CSE will allow Z to be hoisted.
+;;;
+;;; Peeling does cause code growth. If this becomes a problem we will
+;;; need to apply heuristics to limit its applicability.
+;;;
+;;; Implementation-wise, things are complicated by values flowing out of
+;;; the loop. We actually perform this transformation only on loops
+;;; that have a single exit continuation, so that we define values
+;;; flowing out in one place. We rename the loop variables in two
+;;; places internally: one for the peeled iteration, and another for
+;;; the body. The loop variables' original names are then bound in a
+;;; join continuation for use by successor code.
+;;;
+;;; Code:
+
+(define-module (language cps peel-loops)
+ #:use-module (ice-9 match)
+ #:use-module ((srfi srfi-1) #:select (fold))
+ #:use-module (language cps)
+ #:use-module (language cps utils)
+ #:use-module (language cps intmap)
+ #:use-module (language cps intset)
+ #:export (peel-loops))
+
+(define (intset-map f set)
+ (persistent-intmap
+ (intset-fold (lambda (i out) (intmap-add! out i (f i))) set empty-intmap)))
+
+(define (loop-successors scc succs)
+ (intset-subtract (intset-fold (lambda (label exits)
+ (intset-union exits (intmap-ref succs label)))
+ scc empty-intset)
+ scc))
+
+(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 vars)
+ (persistent-intset
+ (fold1 (lambda (var set) (intset-add! set var)) vars empty-intset)))
+
+(define (compute-live-variables cps entry body succs)
+ (let* ((succs (intset-map (lambda (label)
+ (intset-intersect (intmap-ref succs label) body))
+ body))
+ (init (intset-map (lambda (label) #f) body))
+ (kill (intset-map (lambda (label) #f) body))
+ (gen (intset-map (lambda (label)
+ (match (intmap-ref cps label)
+ (($ $kargs names vars) (list->intset vars))
+ (_ empty-intset)))
+ body))
+ (in (intmap-replace init entry (intmap-ref gen entry)))
+ (out init))
+ (define (subtract in kill) (or in empty-intset))
+ (define (add in gen) (if in (intset-union in gen) gen))
+ (define (meet in out) (if in (intset-intersect in out) out))
+ (call-with-values (lambda ()
+ (solve-flow-equations succs in out kill gen
+ subtract add meet
+ (intset entry)))
+ (lambda (in out)
+ out))))
+
+(define (compute-out-vars cps entry body succs exit)
+ (let ((live (compute-live-variables cps entry body succs)))
+ (intset-fold-right
+ cons
+ (intmap-fold (lambda (label succs live-out)
+ (if (intset-ref succs exit)
+ (if live-out
+ (intset-intersect live-out (intmap-ref live label))
+ (intmap-ref live label))
+ live-out))
+ succs #f)
+ '())))
+
+(define (rename-cont cont fresh-labels fresh-vars)
+ (define (rename-label label)
+ (intmap-ref fresh-labels label (lambda (label) label)))
+ (define (rename-var var)
+ (intmap-ref fresh-vars var (lambda (var) var)))
+ (define (rename-exp exp)
+ (rewrite-exp exp
+ ((or ($ $const) ($ $prim) ($ $closure) ($ $rec ())) ,exp)
+ (($ $values args)
+ ($values ,(map rename-var args)))
+ (($ $call proc args)
+ ($call (rename-var proc) ,(map rename-var args)))
+ (($ $callk k proc args)
+ ($callk k (rename-var proc) ,(map rename-var args)))
+ (($ $branch kt ($ $values (arg)))
+ ($branch (rename-label kt) ($values ((rename-var arg)))))
+ (($ $branch kt ($ $primcall name args))
+ ($branch (rename-label kt) ($primcall name ,(map rename-var args))))
+ (($ $primcall name args)
+ ($primcall name ,(map rename-var args)))
+ (($ $prompt escape? tag handler)
+ ($prompt escape? (rename-var tag) (rename-label handler)))))
+ (rewrite-cont cont
+ (($ $kargs names vars ($ $continue k src exp))
+ ($kargs names (map rename-var vars)
+ ($continue (rename-label k) src ,(rename-exp exp))))
+ (($ $kreceive ($ $arity req () rest) kargs)
+ ($kreceive req rest (rename-label kargs)))))
+
+(define (compute-var-names conts)
+ (persistent-intmap
+ (intmap-fold (lambda (label cont out)
+ (match cont
+ (($ $kargs names vars)
+ (fold (lambda (name var out)
+ (intmap-add! out var name))
+ out names vars))
+ (_ out)))
+ conts empty-intmap)))
+
+(define (peel-loop cps entry body-labels succs preds)
+ (let* ((body-conts (intset-map (lambda (label) (intmap-ref cps label))
+ body-labels))
+ (var-names (compute-var-names body-conts))
+ ;; All loop exits branch to this label.
+ (exit (trivial-intset (loop-successors body-labels succs)))
+ ;; The variables that flow out of the loop, as a list.
+ (out-vars (compute-out-vars cps entry body-labels succs exit))
+ (out-names (map (lambda (var) (intmap-ref var-names var)) out-vars))
+ (join-label (fresh-label))
+ (join-cont (build-cont
+ ($kargs out-names out-vars
+ ($continue exit #f ($values ())))))
+ (trampoline-cont
+ ;; A $values predecessor for the join, passing the out-vars
+ ;; using their original names. These will get renamed in
+ ;; both the peeled iteration and the body.
+ (build-cont
+ ($kargs () ()
+ ($continue join-label #f ($values out-vars)))))
+ (fresh-body-labels
+ ;; Fresh labels for the body.
+ (intset-map (lambda (old) (fresh-label)) body-labels))
+ (fresh-body-vars
+ ;; Fresh vars for the body.
+ (intmap-map (lambda (var name) (fresh-var)) var-names))
+ (fresh-body-entry
+ ;; The name of the entry, but in the body.
+ (intmap-ref fresh-body-labels entry))
+ (fresh-peeled-vars
+ ;; Fresh names for variables that flow out of the peeled iteration.
+ (fold1 (lambda (var out) (intmap-add out var (fresh-var)))
+ out-vars empty-intmap))
+ (peeled-trampoline-label
+ ;; Label for trampoline to pass values out of the peeled
+ ;; iteration.
+ (fresh-label))
+ (peeled-trampoline-cont
+ ;; Trampoline for the peeled iteration, ready to adjoin to
+ ;; CPS.
+ (rename-cont trampoline-cont empty-intmap fresh-peeled-vars))
+ (peeled-labels
+ ;; Exit goes to trampoline, back edges to body.
+ (intmap-add (intmap-add empty-intmap exit peeled-trampoline-label)
+ entry fresh-body-entry))
+ (peeled-iteration
+ ;; The peeled iteration.
+ (intmap-map (lambda (label cont)
+ (rename-cont cont peeled-labels fresh-peeled-vars))
+ body-conts))
+ (body-trampoline-label
+ ;; Label for trampoline to pass values out of the body.
+ (fresh-label))
+ (body-trampoline-cont
+ ;; Trampoline for the body, ready to adjoin to CPS.
+ (rename-cont trampoline-cont empty-intmap fresh-body-vars))
+ (fresh-body
+ ;; The body, renamed.
+ (let ((label-map (intmap-add fresh-body-labels
+ exit body-trampoline-label)))
+ (persistent-intmap
+ (intmap-fold
+ (lambda (label new-label out)
+ (intmap-add! out new-label
+ (rename-cont (intmap-ref body-conts label)
+ label-map fresh-body-vars)))
+ fresh-body-labels empty-intmap)))))
+
+ (let* ((cps (intmap-add! cps join-label join-cont))
+ (cps (intmap-add! cps peeled-trampoline-label
+ peeled-trampoline-cont))
+ (cps (intmap-add! cps body-trampoline-label
+ body-trampoline-cont))
+ (cps (intmap-fold (lambda (label cont cps)
+ (intmap-replace! cps label cont))
+ peeled-iteration cps))
+ (cps (intmap-fold (lambda (label cont cps)
+ (intmap-add! cps label cont))
+ fresh-body cps)))
+ cps)))
+
+(define (peel-loops-in-function kfun body cps)
+ (let* ((succs (compute-successors cps kfun))
+ (preds (invert-graph succs)))
+ ;; We can peel if there is one successor to the loop, and if the
+ ;; loop has no nested functions. (Peeling a nested function would
+ ;; cause exponential code growth.)
+ (define (can-peel? body)
+ (and (trivial-intset (loop-successors body succs))
+ (intset-fold (lambda (label peel?)
+ (match (intmap-ref cps label)
+ (($ $kargs _ _ ($ $continue _ _ exp))
+ (match exp
+ (($ $fun) #f)
+ (($ $rec (_ . _)) #f)
+ (_ peel?)))
+ (_ peel?)))
+ body #t)))
+
+ (intmap-fold
+ (lambda (id scc cps)
+ (cond
+ ((trivial-intset scc) cps)
+ ((find-entry scc preds)
+ => (lambda (entry)
+ (if (can-peel? scc)
+ (peel-loop cps entry scc succs preds)
+ cps)))
+ (else cps)))
+ (compute-strongly-connected-components succs kfun)
+ cps)))
+
+(define (peel-loops cps)
+ (persistent-intmap
+ (with-fresh-name-state cps
+ (intmap-fold peel-loops-in-function
+ (compute-reachable-functions cps)
+ cps))))
From 90c11483e630dd4f1d04feae9d370304237aa6cb Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 27 Jul 2015 14:53:59 +0200
Subject: [PATCH 044/865] Better codegen for $values terms that don't shuffle
* module/language/cps/compile-bytecode.scm (compute-forwarding-labels):
Analyze forwarding labels before emitting code. This lets us elide
conts that cause no shuffles, allowing more fallthrough.
---
module/language/cps/compile-bytecode.scm | 186 +++++++++++++----------
1 file changed, 107 insertions(+), 79 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index a57074380..265189b17 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -55,9 +55,42 @@
set
empty-intmap)))
+;; Any $values expression that continues to a $kargs and causes no
+;; shuffles is a forwarding label.
+(define (compute-forwarding-labels cps allocation)
+ (fixpoint
+ (lambda (forwarding-map)
+ (intmap-fold (lambda (label target forwarding-map)
+ (let ((new-target (intmap-ref forwarding-map target
+ (lambda (target) target))))
+ (if (eqv? target new-target)
+ forwarding-map
+ (intmap-replace forwarding-map label new-target))))
+ forwarding-map forwarding-map))
+ (intmap-fold (lambda (label cont forwarding-labels)
+ (match cont
+ (($ $kargs _ _ ($ $continue k _ ($ $values)))
+ (match (lookup-parallel-moves label allocation)
+ (()
+ (match (intmap-ref cps k)
+ (($ $ktail) forwarding-labels)
+ (_ (intmap-add forwarding-labels label k))))
+ (_ forwarding-labels)))
+ (_ forwarding-labels)))
+ cps empty-intmap)))
+
(define (compile-function cps asm)
- (let ((allocation (allocate-slots cps))
- (frame-size #f))
+ (let* ((allocation (allocate-slots cps))
+ (forwarding-labels (compute-forwarding-labels cps allocation))
+ (frame-size #f))
+ (define (forward-label k)
+ (intmap-ref forwarding-labels k (lambda (k) k)))
+
+ (define (elide-cont? label)
+ (match (intmap-ref forwarding-labels label (lambda (_) #f))
+ (#f #f)
+ (target (not (eqv? label target)))))
+
(define (maybe-slot sym)
(lookup-maybe-slot sym allocation))
@@ -242,18 +275,6 @@
(($ $primcall 'unwind ())
(emit-unwind asm))))
- (define (forward-label label seen)
- (if (memv label seen)
- label
- (match (intmap-ref cps label)
- (($ $kargs _ _ ($ $continue k _ ($ $values)))
- (match (lookup-parallel-moves label allocation)
- (() (match (intmap-ref cps k)
- (($ $ktail) label)
- (_ (forward-label k (cons label seen)))))
- (_ label)))
- (cont label))))
-
(define (compile-values label exp syms)
(match exp
(($ $values args)
@@ -262,62 +283,60 @@
(lookup-parallel-moves label allocation)))))
(define (compile-test label exp kt kf next-label)
- (let* ((kt (forward-label kt '()))
- (kf (forward-label kf '())))
- (define (prefer-true?)
- (if (< (max kt kf) label)
- ;; Two backwards branches. Prefer
- ;; the nearest.
- (> kt kf)
- ;; Otherwise prefer a backwards
- ;; branch or a near jump.
- (< kt kf)))
- (define (unary op sym)
- (cond
- ((eq? kt next-label)
- (op asm (slot sym) #t kf))
- ((eq? kf next-label)
- (op asm (slot sym) #f kt))
- (else
- (let ((invert? (not (prefer-true?))))
- (op asm (slot sym) invert? (if invert? kf kt))
- (emit-br asm (if invert? kt kf))))))
- (define (binary op a b)
- (cond
- ((eq? kt next-label)
- (op asm (slot a) (slot b) #t kf))
- ((eq? kf next-label)
- (op asm (slot a) (slot b) #f kt))
- (else
- (let ((invert? (not (prefer-true?))))
- (op asm (slot a) (slot b) invert? (if invert? kf kt))
- (emit-br asm (if invert? kt kf))))))
- (match exp
- (($ $values (sym)) (unary emit-br-if-true sym))
- (($ $primcall 'null? (a)) (unary emit-br-if-null a))
- (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
- (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
- (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
- (($ $primcall 'char? (a)) (unary emit-br-if-char a))
- (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
- (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
- (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
- (($ $primcall 'string? (a)) (unary emit-br-if-string a))
- (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
- (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
- (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
- ;; Add more TC7 tests here. Keep in sync with
- ;; *branching-primcall-arities* in (language cps primitives) and
- ;; the set of macro-instructions in assembly.scm.
- (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
- (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
- (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
- (($ $primcall '< (a b)) (binary emit-br-if-< a b))
- (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
- (($ $primcall '= (a b)) (binary emit-br-if-= a b))
- (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
- (($ $primcall '> (a b)) (binary emit-br-if-< b a))
- (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b)))))
+ (define (prefer-true?)
+ (if (< (max kt kf) label)
+ ;; Two backwards branches. Prefer
+ ;; the nearest.
+ (> kt kf)
+ ;; Otherwise prefer a backwards
+ ;; branch or a near jump.
+ (< kt kf)))
+ (define (unary op sym)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot sym) #t kf))
+ ((eq? kf next-label)
+ (op asm (slot sym) #f kt))
+ (else
+ (let ((invert? (not (prefer-true?))))
+ (op asm (slot sym) invert? (if invert? kf kt))
+ (emit-br asm (if invert? kt kf))))))
+ (define (binary op a b)
+ (cond
+ ((eq? kt next-label)
+ (op asm (slot a) (slot b) #t kf))
+ ((eq? kf next-label)
+ (op asm (slot a) (slot b) #f kt))
+ (else
+ (let ((invert? (not (prefer-true?))))
+ (op asm (slot a) (slot b) invert? (if invert? kf kt))
+ (emit-br asm (if invert? kt kf))))))
+ (match exp
+ (($ $values (sym)) (unary emit-br-if-true sym))
+ (($ $primcall 'null? (a)) (unary emit-br-if-null a))
+ (($ $primcall 'nil? (a)) (unary emit-br-if-nil a))
+ (($ $primcall 'pair? (a)) (unary emit-br-if-pair a))
+ (($ $primcall 'struct? (a)) (unary emit-br-if-struct a))
+ (($ $primcall 'char? (a)) (unary emit-br-if-char a))
+ (($ $primcall 'symbol? (a)) (unary emit-br-if-symbol a))
+ (($ $primcall 'variable? (a)) (unary emit-br-if-variable a))
+ (($ $primcall 'vector? (a)) (unary emit-br-if-vector a))
+ (($ $primcall 'string? (a)) (unary emit-br-if-string a))
+ (($ $primcall 'bytevector? (a)) (unary emit-br-if-bytevector a))
+ (($ $primcall 'bitvector? (a)) (unary emit-br-if-bitvector a))
+ (($ $primcall 'keyword? (a)) (unary emit-br-if-keyword a))
+ ;; Add more TC7 tests here. Keep in sync with
+ ;; *branching-primcall-arities* in (language cps primitives) and
+ ;; the set of macro-instructions in assembly.scm.
+ (($ $primcall 'eq? (a b)) (binary emit-br-if-eq a b))
+ (($ $primcall 'eqv? (a b)) (binary emit-br-if-eqv a b))
+ (($ $primcall 'equal? (a b)) (binary emit-br-if-equal a b))
+ (($ $primcall '< (a b)) (binary emit-br-if-< a b))
+ (($ $primcall '<= (a b)) (binary emit-br-if-<= a b))
+ (($ $primcall '= (a b)) (binary emit-br-if-= a b))
+ (($ $primcall '>= (a b)) (binary emit-br-if-<= b a))
+ (($ $primcall '> (a b)) (binary emit-br-if-< b a))
+ (($ $primcall 'logtest (a b)) (binary emit-br-if-logtest a b))))
(define (compile-trunc label k exp nreq rest-var)
(define (do-call proc args emit-call)
@@ -359,13 +378,17 @@
(lambda (asm proc-slot nargs)
(emit-call-label asm proc-slot nargs k))))))
+ (define (skip-elided-conts label)
+ (if (elide-cont? label)
+ (skip-elided-conts (1+ label))
+ label))
+
(define (compile-expression label k exp)
- (let* ((fallthrough? (= k (1+ label))))
+ (let* ((forwarded-k (forward-label k))
+ (fallthrough? (= forwarded-k (skip-elided-conts (1+ label)))))
(define (maybe-emit-jump)
- (unless (or fallthrough?
- (= (forward-label k '())
- (forward-label (1+ label) '())))
- (emit-br asm k)))
+ (unless fallthrough?
+ (emit-br asm forwarded-k)))
(match (intmap-ref cps k)
(($ $ktail)
(compile-tail label exp))
@@ -377,7 +400,8 @@
(($ $kargs () ())
(match exp
(($ $branch kt exp)
- (compile-test label exp kt k (1+ label)))
+ (compile-test label exp (forward-label kt) forwarded-k
+ (skip-elided-conts (1+ label))))
(_
(compile-effect label exp k)
(maybe-emit-jump))))
@@ -389,8 +413,11 @@
(and rest
(match (intmap-ref cps kargs)
(($ $kargs names (_ ... rest)) rest))))
- (unless (and fallthrough? (= kargs (1+ k)))
- (emit-br asm kargs))))))
+ (let* ((kargs (forward-label kargs))
+ (fallthrough? (and fallthrough?
+ (= kargs (skip-elided-conts (1+ k))))))
+ (unless fallthrough?
+ (emit-br asm kargs)))))))
(define (compile-cont label cont)
(match cont
@@ -421,7 +448,8 @@
names vars)
(when src
(emit-source asm src))
- (compile-expression label k exp))
+ (unless (elide-cont? label)
+ (compile-expression label k exp)))
(($ $kreceive arity kargs)
(emit-label asm label))
(($ $ktail)
From 48412395c6501d0f4f98494cfe20c1c18b14d7a6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Mon, 27 Jul 2015 15:11:09 +0200
Subject: [PATCH 045/865] Add closure effects
* module/language/cps/effects-analysis.scm: Add closure effects, to
enable hoisting/CSE of free-ref/free-set!.
---
module/language/cps/effects-analysis.scm | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/module/language/cps/effects-analysis.scm b/module/language/cps/effects-analysis.scm
index af1a5292e..778855de5 100644
--- a/module/language/cps/effects-analysis.scm
+++ b/module/language/cps/effects-analysis.scm
@@ -63,6 +63,7 @@
&struct
&string
&bytevector
+ &closure
&object
&field
@@ -180,7 +181,10 @@
;; Indicates that an expression depends on the contents of a
;; bytevector. We cannot be more precise, as bytevectors may alias
;; other bytevectors.
- &bytevector)
+ &bytevector
+
+ ;; Indicates a dependency on a free variable of a closure.
+ &closure)
(define-inlinable (&field kind field)
(ash (logior (ash field &memory-kind-bits) kind) &effect-kind-bits))
@@ -373,6 +377,17 @@ is or might be a read or a write to the same location as A."
((bv-f32-set! bv n x) (&write-object &bytevector) &type-check)
((bv-f64-set! bv n x) (&write-object &bytevector) &type-check))
+;; Closures.
+(define (closure-field n constants)
+ (indexed-field &closure n constants))
+(define (read-closure-field n constants)
+ (logior &read (closure-field n constants)))
+(define (write-closure-field n constants)
+ (logior &write (closure-field n constants)))
+(define-primitive-effects* constants
+ ((free-ref closure idx) (read-closure-field idx constants))
+ ((free-set! closure idx val) (write-closure-field idx constants)))
+
;; Modules.
(define-primitive-effects
((current-module) (&read-object &module))
From 00884bb79fff41fdf5f22f24a74e366a94a14c9b Mon Sep 17 00:00:00 2001
From: Mark H Weaver
Date: Thu, 13 Aug 2015 01:31:36 -0400
Subject: [PATCH 046/865] Allow decoding of UTF-8 containing U+FFFD, the
replacement character.
* libguile/strings.c (scm_from_utf8_stringn): Use 'u8_mbtoucr' and check
for a decoding error by its 'nbytes' return value. Previously we used
'u8_mbtouc' and improperly assumed that a U+FFFD character indicated a
decoding error.
* libguile/symbols.c (utf8_string_equals_wide_string): Likewise.
* test-suite/tests/bytevectors.test (exception:decoding-error): New
variable.
("2.9 Operations on Strings"): Add tests.
---
libguile/strings.c | 7 ++++---
libguile/symbols.c | 8 ++++----
test-suite/tests/bytevectors.test | 14 +++++++++++++-
3 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/libguile/strings.c b/libguile/strings.c
index 90dc83a66..ee43e815e 100644
--- a/libguile/strings.c
+++ b/libguile/strings.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1995,1996,1998,2000,2001, 2004, 2006, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+/* Copyright (C) 1995, 1996, 1998, 2000, 2001, 2004, 2006,
+ * 2008-2015 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
@@ -1673,9 +1674,9 @@ scm_from_utf8_stringn (const char *str, size_t len)
ascii = 0;
- nbytes = u8_mbtouc (&c, ustr + i, len - i);
+ nbytes = u8_mbtoucr (&c, ustr + i, len - i);
- if (c == 0xfffd)
+ if (nbytes < 0)
/* Bad UTF-8. */
decoding_error (__func__, errno, str, len);
diff --git a/libguile/symbols.c b/libguile/symbols.c
index f93833b9d..71d982730 100644
--- a/libguile/symbols.c
+++ b/libguile/symbols.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2003, 2004,
- * 2006, 2009, 2011, 2013 Free Software Foundation, Inc.
+/* Copyright (C) 1995-1998, 2000, 2001, 2003, 2004, 2006, 2009, 2011,
+ * 2013, 2015 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
@@ -164,10 +164,10 @@ utf8_string_equals_wide_string (const scm_t_uint8 *narrow, size_t nlen,
ucs4_t c;
int nbytes;
- nbytes = u8_mbtouc (&c, narrow + byte_idx, nlen - byte_idx);
+ nbytes = u8_mbtoucr (&c, narrow + byte_idx, nlen - byte_idx);
if (nbytes == 0)
break;
- else if (c == 0xfffd)
+ else if (nbytes < 0)
/* Bad UTF-8. */
return 0;
else if (c != wide[char_idx])
diff --git a/test-suite/tests/bytevectors.test b/test-suite/tests/bytevectors.test
index 4cc5b67e0..f8f020a30 100644
--- a/test-suite/tests/bytevectors.test
+++ b/test-suite/tests/bytevectors.test
@@ -1,6 +1,6 @@
;;;; bytevectors.test --- R6RS bytevectors. -*- mode: scheme; coding: utf-8; -*-
;;;;
-;;;; Copyright (C) 2009-2014 Free Software Foundation, Inc.
+;;;; Copyright (C) 2009-2015 Free Software Foundation, Inc.
;;;;
;;;; Ludovic Courtès
;;;;
@@ -24,6 +24,9 @@
:use-module (rnrs bytevectors)
:use-module (srfi srfi-4))
+(define exception:decoding-error
+ (cons 'decoding-error "input (locale conversion|decoding) error"))
+
;;; Some of the tests in here are examples taken from the R6RS Standard
;;; Libraries document.
@@ -501,6 +504,15 @@
(= (string-length str)
(- (bytevector-length utf8) 2)))))
+ (pass-if-equal "utf8->string [replacement character]"
+ '(104 105 65533)
+ (map char->integer
+ (string->list (utf8->string #vu8(104 105 239 191 189)))))
+
+ (pass-if-exception "utf8->string [invalid encoding]"
+ exception:decoding-error
+ (utf8->string #vu8(104 105 239 191 50)))
+
(pass-if "utf16->string"
(let* ((utf16 (uint-list->bytevector (map char->integer
(string->list "hello, world"))
From 78fdc3e6731df78022345a2680c5c6a18115388a Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 17 Sep 2015 12:45:36 +0200
Subject: [PATCH 047/865] Remove unused (language tree-il inline) module.
* module/language/tree-il/inline.scm: Remove.
* module/Makefile.am (TREE_IL_LANG_SOURCES): Remove inline.scm.
---
module/Makefile.am | 1 -
module/language/tree-il/inline.scm | 25 -------------------------
2 files changed, 26 deletions(-)
delete mode 100644 module/language/tree-il/inline.scm
diff --git a/module/Makefile.am b/module/Makefile.am
index 3dc6117d3..17e663241 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -115,7 +115,6 @@ TREE_IL_LANG_SOURCES = \
language/tree-il/optimize.scm \
language/tree-il/canonicalize.scm \
language/tree-il/analyze.scm \
- language/tree-il/inline.scm \
language/tree-il/compile-cps.scm \
language/tree-il/debug.scm \
language/tree-il/spec.scm
diff --git a/module/language/tree-il/inline.scm b/module/language/tree-il/inline.scm
deleted file mode 100644
index 5a2d9af55..000000000
--- a/module/language/tree-il/inline.scm
+++ /dev/null
@@ -1,25 +0,0 @@
-;;; a simple inliner
-
-;; Copyright (C) 2009, 2010, 2011 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
-
-(define-module (language tree-il inline)
- #:export (inline!))
-
-(define (inline! x)
- (issue-deprecation-warning
- "`inline!' is deprecated. Use (language tree-il peval) instead.")
- x)
From d701e8a3d36cb18096042413bd60b0993845beea Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 17 Sep 2015 20:14:35 +0200
Subject: [PATCH 048/865] Update CPS language documentation
* doc/ref/compiler.texi (Continuation-Passing Style): Update to latest
CPS language.
---
doc/ref/compiler.texi | 507 +++++++++++++++++++++++++++++-------------
1 file changed, 353 insertions(+), 154 deletions(-)
diff --git a/doc/ref/compiler.texi b/doc/ref/compiler.texi
index 9743c5357..75fd4e5cb 100644
--- a/doc/ref/compiler.texi
+++ b/doc/ref/compiler.texi
@@ -513,12 +513,8 @@ Optimization passes performed on Tree-IL currently include:
and calls to primitives to primcalls)
@item Partial evaluation (comprising inlining, copy propagation, and
constant folding)
-@item Common subexpression elimination (CSE)
@end itemize
-In the future, we will move the CSE pass to operate over the lower-level
-CPS language.
-
@node Continuation-Passing Style
@subsection Continuation-Passing Style
@@ -534,6 +530,7 @@ compiler.
* An Introduction to CPS::
* CPS in Guile::
* Building CPS::
+* CPS Soup::
* Compiling CPS::
@end menu
@@ -624,12 +621,57 @@ details manifest, and gives them names.
@node CPS in Guile
@subsubsection CPS in Guile
-Guile's CPS language is composed of @dfn{terms}, @dfn{expressions},
-and @dfn{continuations}.
+@cindex continuation, CPS
+Guile's CPS language is composed of @dfn{continuations}. A continuation
+is a labelled program point. If you are used to traditional compilers,
+think of a continuation as a trivial basic block. A program is a
+``soup'' of continuations, represented as a map from labels to
+continuations.
-A term can either evaluate an expression and pass the resulting values
-to some continuation, or it can declare local continuations and contain
-a sub-term in the scope of those continuations.
+@cindex term, CPS
+@cindex expression, CPS
+Like basic blocks, each continuation belongs to only one function. Some
+continuations are special, like the continuation corresponding to a
+function's entry point, or the continuation that represents the tail of
+a function. Others contain a @dfn{term}. A term contains an
+@dfn{expression}, which evaluates to zero or more values. The term also
+records the continuation to which it will pass its values. Some terms,
+like conditional branches, may continue to one of a number of
+continuations.
+
+Continuation labels are small integers. This makes it easy to sort them
+and to group them into sets. Whenever a term refers to a continuation,
+it does so by name, simply recording the label of the continuation.
+Continuation labels are unique among the set of labels in a program.
+
+Variables are also named by small integers. Variable names are unique
+among the set of variables in a program.
+
+For example, a simple continuation that receives two values and adds
+them together can be matched like this, using the @code{match} form from
+@code{(ice-9 match)}:
+
+@smallexample
+(match cont
+ (($ $kargs (x-name y-name) (x-var y-var)
+ ($ $continue k src ($ $primcall '+ (x-var y-var))))
+ (format #t "Add ~a and ~a and pass the result to label ~a"
+ x-var y-var k)))
+@end smallexample
+
+Here we see the most common kind of continuation, @code{$kargs}, which
+binds some number of values to variables and then evaluates a term.
+
+@deftp {CPS Continuation} $kargs names vars term
+Bind the incoming values to the variables @var{vars}, with original
+names @var{names}, and then evaluate @var{term}.
+@end deftp
+
+The @var{names} of a @code{$kargs} are just for debugging, and will end
+up residualized in the object file for use by the debugger.
+
+The @var{term} in a @code{$kargs} is always a @code{$continue}, which
+evaluates an expression and continues to a continuation.
@deftp {CPS Term} $continue k src exp
Evaluate the expression @var{exp} and pass the resulting values (if any)
@@ -639,44 +681,33 @@ as in @code{source-properties} or is @code{#f} if there is no associated
source.
@end deftp
-@deftp {CPS Term} $letk conts body
-Bind @var{conts}, a list of continuations (@code{$cont} instances), in
-the scope of the sub-term @var{body}. The continuations are mutually
-recursive.
+There are a number of expression kinds. Above you see an example of
+@code{$primcall}.
+
+@deftp {CPS Expression} $primcall name args
+Perform the primitive operation identified by @code{name}, a well-known
+symbol, passing it the arguments @var{args}, and pass all resulting
+values to the continuation. The set of available primitives includes
+all primitives known to Tree-IL and then some more; see the source code
+for details.
@end deftp
-Additionally, the early stages of CPS allow for a set of mutually
-recursive functions to be declared as a term. This @code{$letrec} type
-is like Tree-IL's @code{}. The contification pass will attempt to
-transform the functions declared in a @code{$letrec} into local
-continuations. Any remaining functions are later lowered to @code{$fun}
-expressions.
-
-@deftp {CPS Term} $letrec names syms funs body
-Declare the mutually recursive set of functions denoted by @var{names},
-@var{syms}, and @var{funs} within the sub-term @var{body}. @var{names}
-and @var{syms} are lists of symbols, and @var{funs} is a list of
-@code{$fun} values. @var{syms} are globally unique.
-@end deftp
-
-A higher-order CPS program is a @code{$cont} containing a @code{$kfun}
-(see below), and the @code{$kfun} which contains clauses and those
-clauses contain terms. A first-order CPS program, on the other hand, is
-the result of closure conversion and does not contain nested functions.
-Closure conversion lifts code for all functions up to the top, collects
-their entry continuations as a list of @code{$cont} @code{$kfun}
-instances and binds them in a @code{$program}.
-
-@deftp {CPS Term} $program funs
-A first-order CPS term declaring a recursive scope for first-order
-functions in a compilation unit. @var{funs} is a list of @code{$cont}
-@code{$kfun} instances. The first entry in the list is the entry
-function for the program.
-@end deftp
+@cindex dominate, CPS
+The variables that are used by @code{$primcall}, or indeed by any
+expression, must be defined before the expression is evaluated. An
+equivalent way of saying this is that predecessor @code{$kargs}
+continuation(s) that bind the variables(s) used by the expression must
+@dfn{dominate} the continuation that uses the expression: definitions
+dominate uses. This condition is trivially satisfied in our example
+above, but in general to determine the set of variables that are in
+``scope'' for a given term, you need to do a flow analysis to see what
+continuations dominate a term. The variables that are in scope are
+those variables defined by the continuations that dominate a term.
Here is an inventory of the kinds of expressions in Guile's CPS
-language. Recall that all expressions are wrapped in a @code{$continue}
-term which specifies their continuation.
+language, besides @code{$primcall} which has already been described.
+Recall that all expressions are wrapped in a @code{$continue} term which
+specifies their continuation.
@deftp {CPS Expression} $const val
Continue with the constant value @var{val}.
@@ -687,47 +718,11 @@ Continue with the procedure that implements the primitive operation
named by @var{name}.
@end deftp
-@deftp {CPS Expression} $fun free body
-Continue with a procedure. @var{free} is a list of free variables
-accessed by the procedure. Early CPS uses an empty list for @var{free};
-only after closure conversion is it correctly populated. Finally,
-@var{body} is the @code{$kfun} @code{$cont} of the procedure entry.
-@end deftp
-
-@code{$fun} is part of higher-level CPS. After closure conversion,
-@code{$fun} instances are given a concrete representation. By default,
-a closure is represented as an object built by a @code{$closure}
-expression
-
-@deftp {CPS Expression} $closure label nfree
-Build a closure that joins the code at the continuation named
-@var{label} with space for @var{nfree} free variables. The variables
-will be initialized later via @code{free-variable-set!} primcalls.
-@end deftp
-
-If the closure can be proven to never escape its scope then other
-lighter-weight representations can be chosen.
-
@deftp {CPS Expression} $call proc args
-@deftpx {CPS Expression} $callk label proc args
Call @var{proc} with the arguments @var{args}, and pass all values to
the continuation. @var{proc} and the elements of the @var{args} list
should all be variable names. The continuation identified by the term's
@var{k} should be a @code{$kreceive} or a @code{$ktail} instance.
-
-@code{$callk} is for the case where the call target is known to be in
-the same compilation unit. @var{label} should be some continuation
-label, though it need not be in scope. In this case the @var{proc} is
-simply an additional argument, since it is not used to determine the
-call target at run-time.
-@end deftp
-
-@deftp {CPS Expression} $primcall name args
-Perform the primitive operation identified by @code{name}, a well-known
-symbol, passing it the arguments @var{args}, and pass all resulting
-values to the continuation. The set of available primitives includes
-all primitives known to Tree-IL and then some more; see the source code
-for details.
@end deftp
@deftp {CPS Expression} $values args
@@ -736,7 +731,8 @@ Pass the values named by the list @var{args} to the continuation.
@deftp {CPS Expression} $branch kt exp
Evaluate the branching expression @var{exp}, and continue to @var{kt}
-with zero values if the test evaluates to true. Otherwise, in the false
+with zero values if the test evaluates to true. Otherwise continue to
+the continuation named in the outer @code{$continue} term.
Only certain expressions are valid in a @var{$branch}. Compiling a
@code{$branch} avoids allocating space for the test variable, so the
@@ -744,9 +740,9 @@ expression should be evaluatable without temporary values. In practice
this condition is true for @code{$primcall}s to @code{null?}, @code{=},
and similar primitives that have corresponding @code{br-if-@var{foo}} VM
operations; see the source code for full details. When in doubt, bind
-the test expression to a variable, and reference the variable in the
-@code{$branch} expression. The optimizer should inline the reference if
-possible.
+the test expression to a variable, and branch on a @code{$values}
+expression that references that variable. The optimizer should inline
+the reference if possible.
@end deftp
@deftp {CPS Expression} $prompt escape? tag handler
@@ -758,30 +754,73 @@ the continuation labelled @var{handler}, which should be a
@code{pop-prompt} primcalls.
@end deftp
-The remaining element of the CPS language in Guile is the continuation.
-In CPS, all continuations have unique labels. Since this aspect is
-common to all continuation types, all continuations are contained in a
-@code{$cont} instance:
+@cindex higher-order CPS
+@cindex CPS, higher-order
+@cindex first-order CPS
+@cindex CPS, first-order
+There are two sub-languages of CPS, @dfn{higher-order CPS} and
+@dfn{first-order CPS}. The difference is that in higher-order CPS,
+there are @code{$fun} and @code{$rec} expressions that bind functions or
+mutually-recursive functions in the implicit scope of their use sites.
+Guile transforms higher-order CPS into first-order CPS by @dfn{closure
+conversion}, which chooses representations for all closures and which
+arranges to access free variables through the implicit closure parameter
+that is passed to every function call.
-@deftp {CPS Continuation Wrapper} $cont k cont
-Declare a continuation labelled @var{k}. All references to the
-continuation will use this label.
+@deftp {CPS Expression} $fun body
+Continue with a procedure. @var{body} names the entry point of the
+function, which should be a @code{$kfun}. This expression kind is only
+valid in higher-order CPS, which is the CPS language before closure
+conversion.
@end deftp
-The most common kind of continuation binds some number of values, and
-then evaluates a sub-term. @code{$kargs} is this kind of simple
-@code{lambda}.
-
-@deftp {CPS Continuation} $kargs names syms body
-Bind the incoming values to the variables @var{syms}, with original
-names @var{names}, and then evaluate the sub-term @var{body}.
+@deftp {CPS Expression} $rec names vars funs
+Continue with a set of mutually recursive procedures denoted by
+@var{names}, @var{vars}, and @var{funs}. @var{names} is a list of
+symbols, @var{vars} is a list of variable names (unique integers), and
+@var{funs} is a list of @code{$fun} values. Note that the @code{$kargs}
+continuation should also define @var{names}/@var{vars} bindings.
@end deftp
-Variable names (the names in the @var{syms} of a @code{$kargs}) should
-be unique among all other variable names. To bind a value to a variable
-and then evaluate some term, you would continue with the value to a
-@code{$kargs} that declares one variable. The bound value would then be
-available for use within the body of the @code{$kargs}.
+The contification pass will attempt to transform the functions declared
+in a @code{$rec} into local continuations. Any remaining @code{$fun}
+instances are later removed by the closure conversion pass. By default,
+a closure is represented as an object built by a @code{$closure}
+expression.
+
+@deftp {CPS Expression} $closure label nfree
+Build a closure that joins the code at the continuation named
+@var{label} with space for @var{nfree} free variables. The variables
+will be initialized later via @code{free-set!} primcalls. This
+expression kind is part of first-order CPS.
+@end deftp
+
+If the closure can be proven to never escape its scope then other
+lighter-weight representations can be chosen. Additionally, if all call
+sites are known, closure conversion will hard-wire the calls by lowering
+@code{$call} to @code{$callk}.
+
+@deftp {CPS Expression} $callk label proc args
+Like @code{$call}, but for the case where the call target is known to be
+in the same compilation unit. @var{label} should denote some
+@code{$kfun} continuation in the program. In this case the @var{proc}
+is simply an additional argument, since it is not used to determine the
+call target at run-time.
+@end deftp
+
+At this point we have described terms, expressions, and the most common
+kind of continuation, @code{$kargs}. @code{$kargs} is used when the
+predecessors of the continuation can be instructed to pass the values
+where the continuation wants them. For example, if a @code{$kargs}
+continuation @var{k} binds a variable @var{v}, and the compiler decides
+to allocate @var{v} to slot 6, all predecessors of @var{k} should put
+the value for @var{v} in slot 6 before jumping to @var{k}. One
+situation in which this isn't possible is receiving values from function
+calls. Guile has a calling convention for functions which currently
+places return values on the stack. A continuation of a call must check
+that the number of values returned from a function matches the expected
+number of values, and then must shuffle or collect those values to named
+variables. @code{$kreceive} denotes this kind of continuation.
@deftp {CPS Continuation} $kreceive arity k
Receive values on the stack. Parse them according to @var{arity}, and
@@ -806,18 +845,18 @@ Note that all of these names with the exception of the @var{var}s in the
@var{kw} list are source names, not unique variable names.
@end deftp
-Additionally, there are three specific kinds of continuations that can
-only be declared at function entries.
+Additionally, there are three specific kinds of continuations that are
+only used in function entries.
@deftp {CPS Continuation} $kfun src meta self tail clauses
Declare a function entry. @var{src} is the source information for the
procedure declaration, and @var{meta} is the metadata alist as described
above in Tree-IL's @code{}. @var{self} is a variable bound to
the procedure being called, and which may be used for self-references.
-@var{tail} declares the @code{$cont} wrapping the @code{$ktail} for this
-function, corresponding to the function's tail continuation.
-@var{clause} is the first @code{$kclause} @code{$cont} instance for the
-first @code{case-lambda} clause in the function, or otherwise @code{#f}.
+@var{tail} is the label of the @code{$ktail} for this function,
+corresponding to the function's tail continuation. @var{clause} is the
+label of the first @code{$kclause} for the first @code{case-lambda}
+clause in the function, or otherwise @code{#f}.
@end deftp
@deftp {CPS Continuation} $ktail
@@ -826,10 +865,10 @@ A tail continuation.
@deftp {CPS Continuation} $kclause arity cont alternate
A clause of a function with a given arity. Applications of a function
-with a compatible set of actual arguments will continue to @var{cont}, a
-@code{$kargs} @code{$cont} instance representing the clause body. If
-the arguments are incompatible, control proceeds to @var{alternate},
-which is a @code{$kclause} @code{$cont} for the next clause, or
+with a compatible set of actual arguments will continue to the
+continuation labelled @var{cont}, a @code{$kargs} instance representing
+the clause body. If the arguments are incompatible, control proceeds to
+@var{alternate}, which is a @code{$kclause} for the next clause, or
@code{#f} if there is no next clause.
@end deftp
@@ -842,41 +881,41 @@ constructors or accessors, or instead of S-expression matching.
Deconstruction and matching is handled adequately by the @code{match}
form from @code{(ice-9 match)}. @xref{Pattern Matching}. Construction
-is handled by a set of mutually recursive builder macros:
-@code{build-cps-term}, @code{build-cps-cont}, and @code{build-cps-exp}.
+is handled by a set of mutually builder macros:
+@code{build-term}, @code{build-cont}, and @code{build-exp}.
-In the following interface definitions, consider variables containing
-@code{cont} to be recursively build by @code{build-cps-cont}, and
-likewise for @code{term} and @code{exp}. Consider any other name to be
-evaluated as a Scheme expression. Many of these forms recognize
-@code{unquote} in some contexts, to splice in a previously-built value;
-see the specifications below for full details.
+In the following interface definitions, consider @code{term} and
+@code{exp} to be built by @code{build-term} or @code{build-exp},
+respectively. Consider any other name to be evaluated as a Scheme
+expression. Many of these forms recognize @code{unquote} in some
+contexts, to splice in a previously-built value; see the specifications
+below for full details.
-@deffn {Scheme Syntax} build-cps-term ,val
-@deffnx {Scheme Syntax} build-cps-term ($letk (cont ...) term)
-@deffnx {Scheme Syntax} build-cps-term ($letrec names syms funs term)
-@deffnx {Scheme Syntax} build-cps-term ($continue k src exp)
-@deffnx {Scheme Syntax} build-cps-term ($program conts)
-@deffnx {Scheme Syntax} build-cps-exp ,val
-@deffnx {Scheme Syntax} build-cps-exp ($const val)
-@deffnx {Scheme Syntax} build-cps-exp ($prim name)
-@deffnx {Scheme Syntax} build-cps-exp ($fun src meta free body)
-@deffnx {Scheme Syntax} build-cps-exp ($call proc (arg ...))
-@deffnx {Scheme Syntax} build-cps-exp ($call proc args)
-@deffnx {Scheme Syntax} build-cps-exp ($primcall name (arg ...))
-@deffnx {Scheme Syntax} build-cps-exp ($primcall name args)
-@deffnx {Scheme Syntax} build-cps-exp ($values (arg ...))
-@deffnx {Scheme Syntax} build-cps-exp ($values args)
-@deffnx {Scheme Syntax} build-cps-exp ($prompt escape? tag handler)
-@deffnx {Scheme Syntax} build-cps-cont ,val
-@deffnx {Scheme Syntax} build-cps-cont (k ($kargs (name ...) (sym ...) term))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kargs names syms term))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kif kt kf))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kreceive req rest kargs))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kentry self tail-cont ,clauses))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kentry self tail-cont (cont ...)))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kclause ,arity cont))
-@deffnx {Scheme Syntax} build-cps-cont (k ($kclause (req opt rest kw aok?) cont))
+@deffn {Scheme Syntax} build-term ,val
+@deffnx {Scheme Syntax} build-term ($continue k src exp)
+@deffnx {Scheme Syntax} build-exp ,val
+@deffnx {Scheme Syntax} build-exp ($const val)
+@deffnx {Scheme Syntax} build-exp ($prim name)
+@deffnx {Scheme Syntax} build-exp ($branch kt exp)
+@deffnx {Scheme Syntax} build-exp ($fun kentry)
+@deffnx {Scheme Syntax} build-exp ($rec names syms funs)
+@deffnx {Scheme Syntax} build-exp ($closure k nfree)
+@deffnx {Scheme Syntax} build-exp ($call proc (arg ...))
+@deffnx {Scheme Syntax} build-exp ($call proc args)
+@deffnx {Scheme Syntax} build-exp ($callk k proc (arg ...))
+@deffnx {Scheme Syntax} build-exp ($callk k proc args)
+@deffnx {Scheme Syntax} build-exp ($primcall name (arg ...))
+@deffnx {Scheme Syntax} build-exp ($primcall name args)
+@deffnx {Scheme Syntax} build-exp ($values (arg ...))
+@deffnx {Scheme Syntax} build-exp ($values args)
+@deffnx {Scheme Syntax} build-exp ($prompt escape? tag handler)
+@deffnx {Scheme Syntax} build-cont ,val
+@deffnx {Scheme Syntax} build-cont ($kargs (name ...) (sym ...) term)
+@deffnx {Scheme Syntax} build-cont ($kargs names syms term)
+@deffnx {Scheme Syntax} build-cont ($kreceive req rest kargs)
+@deffnx {Scheme Syntax} build-cont ($kfun src meta self ktail kclause)
+@deffnx {Scheme Syntax} build-cont ($kclause ,arity kbody kalt)
+@deffnx {Scheme Syntax} build-cont ($kclause (req opt rest kw aok?) kbody)
Construct a CPS term, expression, or continuation.
@end deffn
@@ -886,19 +925,179 @@ There are a few more miscellaneous interfaces as well.
A procedural constructor for @code{$arity} objects.
@end deffn
-@deffn {Scheme Syntax} let-gensyms (sym ...) body ...
-Bind @var{sym...} to fresh names, and evaluate @var{body...}.
-@end deffn
-
-@deffn {Scheme Syntax} rewrite-cps-term val (pat term) ...
-@deffnx {Scheme Syntax} rewrite-cps-exp val (pat exp) ...
-@deffnx {Scheme Syntax} rewrite-cps-cont val (pat cont) ...
+@deffn {Scheme Syntax} rewrite-term val (pat term) ...
+@deffnx {Scheme Syntax} rewrite-exp val (pat exp) ...
+@deffnx {Scheme Syntax} rewrite-cont val (pat cont) ...
Match @var{val} against the series of patterns @var{pat...}, using
@code{match}. The body of the matching clause should be a template in
-the syntax of @code{build-cps-term}, @code{build-cps-exp}, or
-@code{build-cps-cont}, respectively.
+the syntax of @code{build-term}, @code{build-exp}, or @code{build-cont},
+respectively.
@end deffn
+@node CPS Soup
+@subsubsection CPS Soup
+
+We describe programs in Guile's CPS language as being a kind of ``soup''
+because all continuations in the program are mixed into the same
+``pot'', so to speak. A program in CPS is a map from continuation
+labels to continuation values. As discussed in the introduction, a
+continuation label is an integer. No label may be negative.
+
+As a matter of convention, label 0 should map to the @code{$kfun}
+continuation of the entry to the program, which should be a function of
+no arguments. The body of a function consists of the labelled
+continuations that are reachable from the function entry. A program can
+refer to other functions, either via @code{$fun} and @code{$rec} in
+higher-order CPS, or via @code{$closure} and @code{$callk} in
+first-order CPS. The program logically contains all continuations of
+all functions reachable from the entry function. A compiler pass may
+leave unreachable continuations in a program, but analyses should
+usually either apply only to reachable continuations, or should make
+translations that are oblivious as to whether a continuation is
+reachable or not.
+
+@cindex intmap
+The map itself is implemented as an @dfn{intmap}, a functional
+array-mapped trie specialized for integer keys. Currently intmaps are a
+private data structure only used by the CPS phase of the compiler. To
+work with intmaps, load the @code{(language cps intmap)} module:
+
+@example
+(use-modules (language cps intmap))
+@end example
+
+Intmaps are functional data structures, so there is no constructor as
+such: one can simply start with the empty intmap and add entries to it.
+
+@example
+(intmap? empty-intmap) @result{} #t
+(define x (intmap-add empty-intmap 42 "hi"))
+(intmap? x) @result{} #t
+(intmap-ref x 42) @result{} "hi"
+(intmap-ref x 43) @result{} @i{error: 43 not present}
+(intmap-ref x 43 (lambda (k) "yo!")) @result{} "yo"
+(intmap-add x 42 "hej") @result{} @i{error: 42 already present}
+@end example
+
+@code{intmap-ref} and @code{intmap-add} are the core of the intmap
+interface. There is also @code{intmap-replace}, which replaces the
+value associated with a given key, requiring that the key was present
+already, and @code{intmap-remove}, which removes a key from an intmap.
+
+Intmaps have a tree-like structure that is well-suited to set operations
+such as union and intersection, so there is are also the binary
+@code{intmap-union} and @code{intmap-intersect} procedures. If the
+result is equivalent to either argument, that argument is returned
+as-is; in that way, one can detect whether the set operation produced a
+new result simply by checking with @code{eq?}. This makes intmaps
+useful when computing fixed points.
+
+If a key is present in both intmaps and the key is not the same in the
+sense of @code{eq?}, the resulting value is determined by a ``meet''
+procedure, which is the optional last argument to @code{intmap-union},
+@code{intmap-intersect}, and also to @code{intmap-add},
+@code{intmap-replace}, and similar functions. The meet procedure will
+be called with the two values and should return the intersected or
+unioned value in some appropriate way. If no meet procedure is given,
+the default meet procedure will raise an error.
+
+To traverse over the set of values in an intmap, there are the
+@code{intmap-next} and @code{intmap-prev} procedures. For example, if
+intmap @var{x} has one entry mapping 42 to some value, we would have:
+
+@example
+(intmap-next x) @result{} 42
+(intmap-next x 0) @result{} 42
+(intmap-next x 42) @result{} 42
+(intmap-next x 43) @result{} #f
+(intmap-prev x) @result{} 42
+(intmap-prev x 42) @result{} 42
+(intmap-prev x 41) @result{} #f
+@end example
+
+There is also the @code{intmap-fold} procedure, which folds over keys
+and values in the intmap from lowest to highest value, and
+@code{intmap-fold-right} which does so in the opposite direction. These
+procedures may take up to 3 seed values. The number of values that the
+fold procedure returns is the number of seed values.
+
+@example
+(define q (intmap-add (intmap-add empty-intmap 1 2) 3 4))
+(intmap-fold acons q '()) @result{} ((3 . 4) (1 . 2))
+(intmap-fold-right acons q '()) @result{} ((1 . 2) (3 . 4))
+@end example
+
+When an entry in an intmap is updated (removed, added, or changed), a
+new intmap is created that shares structure with the original intmap.
+This operation ensures that the result of existing computations is not
+affected by future computations: no mutation is ever visible to user
+code. This is a great property in a compiler data structure, as it lets
+us hold a copy of a program before a transformation and use it while we
+build a post-transformation program.
+
+However, the allocation costs are sometimes too much, especially in
+cases when we know that we can just update the intmap in place. As an
+example, say we have an intmap mapping the integers 1 to 100 to the
+integers 42 to 141. Let's say that we want to transform this map by
+adding 1 to each value. There is already an efficient @code{intmap-map}
+procedure in the @code{(language cps utils}) module, but if we didn't
+know about that we might do:
+
+@example
+(define (intmap-increment map)
+ (let lp ((k 0) (map map))
+ (let ((k (intmap-next map k)))
+ (if k
+ (let ((v (intmap-ref map k)))
+ (lp (1+ k) (intmap-replace map k (1+ v))))
+ map))))
+@end example
+
+@cindex intmap, transient
+@cindex transient intmaps
+Observe that the intermediate values created by @code{intmap-replace}
+are completely invisible to the program -- only the last result of
+@code{intmap-replace} value is needed. The rest might as well share
+state with the last one, and we could update in place. Guile allows
+this kind of interface via @dfn{transient intmaps}, inspired by
+Clojure's transient interface (@uref{http://clojure.org/transients}).
+
+The @code{intmap-add!} and @code{intmap-replace!} procedures return
+transient intmaps. If one of these in-place procedures is called on a
+normal persistent intmap, a new transient intmap is created. This is an
+O(1) operation. In all other respects the interface is like their
+persistent counterparts, @code{intmap-add} and @code{intmap-replace}.
+
+If an in-place procedure is called on a transient intmap, the intmap is
+mutated in-place and the same value is returned. If a persistent
+operation like @code{intmap-add} is called on a transient intmap, the
+transient's mutable substructure is then marked as persistent, and
+@code{intmap-add} then runs on a new persistent intmap sharing structure
+but not state with the original transient. Mutating a transient will
+cause enough copying to ensure that it can make its change, but if part
+of its substructure is already ``owned'' by it, no more copying is
+needed.
+
+We can use transients to make @code{intmap-increment} more efficient.
+The two changed elements have been marked @strong{like this}.
+
+@example
+(define (intmap-increment map)
+ (let lp ((k 0) (map map))
+ (let ((k (intmap-next map k)))
+ (if k
+ (let ((v (intmap-ref map k)))
+ (lp (1+ k) (@strong{intmap-replace!} map k (1+ v))))
+ (@strong{persistent-intmap} map)))))
+@end example
+
+Be sure to tag the result as persistent using the
+@code{persistent-intmap} procedure to prevent the mutability from
+leaking to other parts of the program. For added paranoia, you could
+call @code{persistent-intmap} on the incoming map, to ensure that if it
+were already transient, that the mutations in the body of
+@code{intmap-increment} wouldn't affect the incoming value.
+
@node Compiling CPS
@subsubsection Compiling CPS
From 73f6146bd83045dad909f37e42306d05f3bf9d16 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 18 Sep 2015 10:13:35 +0200
Subject: [PATCH 049/865] Minor CPS documentation cleanups
* doc/ref/compiler.texi (Continuation-Passing Style): Minor cleanups.
---
doc/ref/compiler.texi | 104 +++++++++++++++++++++++-------------------
1 file changed, 57 insertions(+), 47 deletions(-)
diff --git a/doc/ref/compiler.texi b/doc/ref/compiler.texi
index 75fd4e5cb..6696360bd 100644
--- a/doc/ref/compiler.texi
+++ b/doc/ref/compiler.texi
@@ -939,7 +939,8 @@ respectively.
We describe programs in Guile's CPS language as being a kind of ``soup''
because all continuations in the program are mixed into the same
-``pot'', so to speak. A program in CPS is a map from continuation
+``pot'', so to speak, without explicit markers as to what function or
+scope a continuation is in. A program in CPS is a map from continuation
labels to continuation values. As discussed in the introduction, a
continuation label is an integer. No label may be negative.
@@ -951,16 +952,18 @@ refer to other functions, either via @code{$fun} and @code{$rec} in
higher-order CPS, or via @code{$closure} and @code{$callk} in
first-order CPS. The program logically contains all continuations of
all functions reachable from the entry function. A compiler pass may
-leave unreachable continuations in a program, but analyses should
-usually either apply only to reachable continuations, or should make
-translations that are oblivious as to whether a continuation is
-reachable or not.
+leave unreachable continuations in a program; subsequent compiler passes
+should ensure that their transformations and analyses only take
+reachable continuations into account. It's OK though if transformation
+runs over all continuations if including the unreachable continuations
+has no effect on the transformations on the live continuations.
@cindex intmap
-The map itself is implemented as an @dfn{intmap}, a functional
-array-mapped trie specialized for integer keys. Currently intmaps are a
-private data structure only used by the CPS phase of the compiler. To
-work with intmaps, load the @code{(language cps intmap)} module:
+The ``soup'' itself is implemented as an @dfn{intmap}, a functional
+array-mapped trie specialized for integer keys. Intmaps associate
+integers with values of any kind. Currently intmaps are a private data
+structure only used by the CPS phase of the compiler. To work with
+intmaps, load the @code{(language cps intmap)} module:
@example
(use-modules (language cps intmap))
@@ -992,14 +995,14 @@ as-is; in that way, one can detect whether the set operation produced a
new result simply by checking with @code{eq?}. This makes intmaps
useful when computing fixed points.
-If a key is present in both intmaps and the key is not the same in the
-sense of @code{eq?}, the resulting value is determined by a ``meet''
-procedure, which is the optional last argument to @code{intmap-union},
-@code{intmap-intersect}, and also to @code{intmap-add},
-@code{intmap-replace}, and similar functions. The meet procedure will
-be called with the two values and should return the intersected or
-unioned value in some appropriate way. If no meet procedure is given,
-the default meet procedure will raise an error.
+If a key is present in both intmaps and the associated values are not
+the same in the sense of @code{eq?}, the resulting value is determined
+by a ``meet'' procedure, which is the optional last argument to
+@code{intmap-union}, @code{intmap-intersect}, and also to
+@code{intmap-add}, @code{intmap-replace}, and similar functions. The
+meet procedure will be called with the two values and should return the
+intersected or unioned value in some domain-specific way. If no meet
+procedure is given, the default meet procedure will raise an error.
To traverse over the set of values in an intmap, there are the
@code{intmap-next} and @code{intmap-prev} procedures. For example, if
@@ -1033,15 +1036,16 @@ This operation ensures that the result of existing computations is not
affected by future computations: no mutation is ever visible to user
code. This is a great property in a compiler data structure, as it lets
us hold a copy of a program before a transformation and use it while we
-build a post-transformation program.
+build a post-transformation program. Updating an intmap is O(log
+@var{n}) in the size of the intmap.
-However, the allocation costs are sometimes too much, especially in
-cases when we know that we can just update the intmap in place. As an
-example, say we have an intmap mapping the integers 1 to 100 to the
-integers 42 to 141. Let's say that we want to transform this map by
-adding 1 to each value. There is already an efficient @code{intmap-map}
-procedure in the @code{(language cps utils}) module, but if we didn't
-know about that we might do:
+However, the O(log @var{n}) allocation costs are sometimes too much,
+especially in cases when we know that we can just update the intmap in
+place. As an example, say we have an intmap mapping the integers 1 to
+100 to the integers 42 to 141. Let's say that we want to transform this
+map by adding 1 to each value. There is already an efficient
+@code{intmap-map} procedure in the @code{(language cps utils}) module,
+but if we didn't know about that we might do:
@example
(define (intmap-increment map)
@@ -1062,21 +1066,21 @@ state with the last one, and we could update in place. Guile allows
this kind of interface via @dfn{transient intmaps}, inspired by
Clojure's transient interface (@uref{http://clojure.org/transients}).
-The @code{intmap-add!} and @code{intmap-replace!} procedures return
-transient intmaps. If one of these in-place procedures is called on a
-normal persistent intmap, a new transient intmap is created. This is an
-O(1) operation. In all other respects the interface is like their
+The in-place @code{intmap-add!} and @code{intmap-replace!} procedures
+return transient intmaps. If one of these in-place procedures is called
+on a normal persistent intmap, a new transient intmap is created. This
+is an O(1) operation. In all other respects the interface is like their
persistent counterparts, @code{intmap-add} and @code{intmap-replace}.
-
If an in-place procedure is called on a transient intmap, the intmap is
-mutated in-place and the same value is returned. If a persistent
-operation like @code{intmap-add} is called on a transient intmap, the
-transient's mutable substructure is then marked as persistent, and
-@code{intmap-add} then runs on a new persistent intmap sharing structure
-but not state with the original transient. Mutating a transient will
-cause enough copying to ensure that it can make its change, but if part
-of its substructure is already ``owned'' by it, no more copying is
-needed.
+mutated in-place and the same value is returned.
+
+If a persistent operation like @code{intmap-add} is called on a
+transient intmap, the transient's mutable substructure is then marked as
+persistent, and @code{intmap-add} then runs on a new persistent intmap
+sharing structure but not state with the original transient. Mutating a
+transient will cause enough copying to ensure that it can make its
+change, but if part of its substructure is already ``owned'' by it, no
+more copying is needed.
We can use transients to make @code{intmap-increment} more efficient.
The two changed elements have been marked @strong{like this}.
@@ -1098,6 +1102,10 @@ call @code{persistent-intmap} on the incoming map, to ensure that if it
were already transient, that the mutations in the body of
@code{intmap-increment} wouldn't affect the incoming value.
+In summary, programs in CPS are intmaps whose values are continuations.
+See the source code of @code{(language cps utils)} for a number of
+useful facilities for working with CPS values.
+
@node Compiling CPS
@subsubsection Compiling CPS
@@ -1114,16 +1122,18 @@ variables (in Tree-IL, locals that are @code{}) are
converted to being boxed values on the heap. @xref{Variables and the
VM}.
-After CPS conversion, Guile runs some optimization passes. The major
-optimization performed on CPS is contification, in which functions that
-are always called with the same continuation are incorporated directly
-into a function's body. This opens up space for more optimizations, and
-turns procedure calls into @code{goto}. It can also make loops out of
-recursive function nests.
+After CPS conversion, Guile runs some optimization passes over the CPS.
+Most optimization in Guile is done on the CPS language. The one major
+exception is partial evaluation, which for historic reasons is done on
+Tree-IL.
-At the time of this writing (2014), most high-level optimization in
-Guile is done on Tree-IL. We would like to rewrite many of these passes
-to operate on CPS instead, as it is easier to reason about CPS.
+The major optimization performed on CPS is contification, in which
+functions that are always called with the same continuation are
+incorporated directly into a function's body. This opens up space for
+more optimizations, and turns procedure calls into @code{goto}. It can
+also make loops out of recursive function nests. Guile also does dead
+code elimination, common subexpression elimination, loop peeling and
+invariant code motion, and range and type inference.
The rest of the optimization passes are really cleanups and
canonicalizations. CPS spans the gap between high-level languages and
From 315adb6347c3e51f4e24fa32ff08458f2aa1b09c Mon Sep 17 00:00:00 2001
From: Mark H Weaver
Date: Tue, 6 Oct 2015 10:09:12 -0400
Subject: [PATCH 050/865] Fix typo in CPS conversion.
Fixes .
Reported by tantalum .
* module/language/tree-il/compile-cps.scm (convert): Add missing 'cps'
argument to the continuation passed to 'convert-arg'.
---
module/language/tree-il/compile-cps.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/language/tree-il/compile-cps.scm b/module/language/tree-il/compile-cps.scm
index 59d2d7d90..7f34e6b48 100644
--- a/module/language/tree-il/compile-cps.scm
+++ b/module/language/tree-il/compile-cps.scm
@@ -460,7 +460,7 @@
(($ src mod name public? exp)
(convert-arg cps exp
- (lambda (val)
+ (lambda (cps val)
(module-box
cps src mod name public? #t
(lambda (cps box)
From a0a8741608a4094cbd0f4d25d6bfdec1ff9e48eb Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 18 Sep 2015 10:43:06 +0200
Subject: [PATCH 051/865] Minor VM documentation updates
* doc/ref/vm.texi (Why a VM?, Variables and the VM): Minor updates.
---
doc/ref/vm.texi | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/doc/ref/vm.texi b/doc/ref/vm.texi
index 6616af446..ba31d7ccb 100644
--- a/doc/ref/vm.texi
+++ b/doc/ref/vm.texi
@@ -80,11 +80,12 @@ but it is not normally used at runtime.)
The upside of implementing the interpreter in Scheme is that we preserve
tail calls and multiple-value handling between interpreted and compiled
-code. The downside is that the interpreter in Guile 2.2 is still slower
-than the interpreter in 1.8. We hope the that the compiler's speed makes
-up for the loss. In any case, once we have native compilation for
-Scheme code, we expect the new self-hosted interpreter to beat the old
-hand-tuned C implementation.
+code. The downside is that the interpreter in Guile 2.2 is still about
+twice as slow as the interpreter in 1.8. Since Scheme users are mostly
+running compiled code, the compiler's speed more than makes up for the
+loss. In any case, once we have native compilation for Scheme code, we
+expect the self-hosted interpreter to handily beat the old hand-tuned C
+implementation.
Also note that this decision to implement a bytecode compiler does not
preclude native compilation. We can compile from bytecode to native
@@ -213,8 +214,9 @@ variables are allocated in ``boxes''---actually, in variable cells.
variables are indirected through the boxes.
Thus perhaps counterintuitively, what would seem ``closer to the
-metal'', viz @code{set!}, actually forces an extra memory allocation
-and indirection.
+metal'', viz @code{set!}, actually forces an extra memory allocation and
+indirection. Sometimes Guile's optimizer can remove this allocation,
+but not always.
Going back to our example, @code{b} may be allocated on the stack, as
it is never mutated.
From d7199da8c9d287b9602e053d470d0a822b4f7cec Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 18 Oct 2015 13:24:21 +0200
Subject: [PATCH 052/865] Fix prompt miscompilation
* module/language/cps/compile-bytecode.scm (compile-function): Fix
miscompilation when the handler body is forwarded.
---
module/language/cps/compile-bytecode.scm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index 265189b17..498bac9b3 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -224,7 +224,7 @@
((src . dst) (emit-mov asm dst src)))
(lookup-parallel-moves handler allocation))
(emit-reset-frame asm frame-size)
- (emit-br asm khandler-body)))))
+ (emit-br asm (forward-label khandler-body))))))
(($ $primcall 'cache-current-module! (sym scope))
(emit-cache-current-module! asm (slot sym) (constant scope)))
(($ $primcall 'free-set! (closure idx value))
From 0007507340b10754cb307763cbc8eeb064853926 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 22 Sep 2015 10:24:30 +0000
Subject: [PATCH 053/865] VM stack grows downward
Adapt VM stack to grow downward. This will make native compilation look
more like the VM code, as we will be able to use native CALL
instructions, taking proper advantage of the return address buffer.
* libguile/continuations.c (scm_i_continuation_to_frame): Record offsets
from stack top.
* libguile/control.c (scm_i_prompt_pop_abort_args_x): Adapt for reversed
order of arguments, and instead of relying on the abort to push on the
number of arguments, make the caller save the stack depth, which
allows us to compute the number of arguments ourselves.
(reify_partial_continuation, scm_c_abort): Adapt to reversed stack
order.
* libguile/dynstack.c (scm_dynstack_wind_prompt): Since we wind the
stack in a downward direction, subtract the reloc instead of adding
it.
* libguile/dynstack.h (SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY): Remove flag;
instead rely on prompt-establishing code to save the stack depth.
* libguile/eval.c (eval): Remove extraneous "volatile" declarations for
variables that are not re-set between the setjmp and any longjmp.
Adapt to save stack depth before instating the prompt.
* libguile/foreign.c (scm_i_foreign_call): Adapt to receive arguments in
reverse order.
* libguile/frames.c (frame_stack_top, scm_i_frame_stack_top): Adapt to
compute stack top instead of stack bottom.
(scm_c_frame_closure): Adapt to stack growth change.
(scm_frame_num_locals, scm_frame_local_ref, scm_frame_set_x): Use
union data type to access stack.
(RELOC): Reformat.
(scm_c_frame_previous): Adapt to stack growth change.
* libguile/frames.h: Adapt stack diagram to indicate that the stack
grows up.
(union scm_vm_stack_element): New data type used to access items on
the stack.
(SCM_FRAME_PREVIOUS_SP)
(SCM_FRAME_RETURN_ADDRESS, SCM_FRAME_SET_RETURN_ADDRESS)
(SCM_FRAME_DYNAMIC_LINK, SCM_FRAME_SET_DYNAMIC_LINK)
(SCM_FRAME_LOCAL, SCM_FRAME_NUM_LOCALS): Adapt to stack representation
change.
(SCM_FRAME_SLOT): New helper.
(SCM_VM_FRAME_FP, SCM_VM_FRAME_SP): Adapt to stack growth change.
* libguile/stacks.c (scm_make_stack): Record offsets from top of stack.
* libguile/throw.c (catch): Adapt to scm_i_prompt_pop_abort_args_x
change.
* libguile/vm-engine.c (ALLOC_FRAME, RESET_FRAME):
(FRAME_LOCALS_COUNT_FROM): Adapt to stack growth change.
(LOCAL_ADDRESS): Use SCM_FRAME_SLOT to get the address as the proper
data type.
(RETURN_ONE_VALUE, RETURN_VALUE_LIST): Adapt to stack growth change.
(apply): Shuffling up the SMOB apply args can cause the stack to
expand, so use ALLOC_FRAME instead of RESET_FRAME.
(vm_engine): Adapt for stack growth change.
* libguile/vm.c (vm_increase_sp, vm_push_sp, vm_restore_sp): Adapt to
stack representation change.
(scm_i_vm_cont_to_frame): Adapt to take offsets from the top.
(scm_i_vm_capture_stack): Adapt to capture from the top.
(vm_return_to_continuation_inner): Adapt for data type changes.
(vm_return_to_continuation): Likewise, and instead of looping, just
splat the saved arguments on with memcpy.
(vm_dispatch_hook): Adapt to receive arguments in the reverse order.
Adapt callers.
(vm_abort): There is never a tail argument. Adapt to stack
representation change.
(vm_reinstate_partial_continuation)
(vm_reinstate_partial_continuation_inner): Adapt to stack growth
change.
(allocate_stack, free_stack): Adapt to data type change.
(expand_stack): Don't try to mremap(), as you can't grow a mapping
from the bottom. Without knowing that there's a free mapping space
right below the old stack, which there usually isn't on Linux, we have
to copy. We can't use MAP_GROWSDOWN because Linux is buggy.
(make_vm): Adapt to stack representation changes.
(return_unused_stack_to_os): Round down instead of up, as the stack
grows down.
(scm_i_vm_mark_stack): Adapt to walk up the stack.
(scm_i_vm_free_stack): Adapt to scm_vm changes.
(vm_expand_stack_inner, reset_stack_limit, vm_expand_stack): Adapt to
the stack growing down.
(scm_call_n): Adapt to the stack growing down. Don't allow argv to
point into the stack.
* libguile/vm.h (struct scm_vm, struct scm_vm_cont): Adapt to hold the
stack top and bottom.
---
libguile/continuations.c | 7 +-
libguile/control.c | 63 +++----
libguile/control.h | 3 +-
libguile/dynstack.c | 4 +-
libguile/dynstack.h | 3 +-
libguile/eval.c | 18 +-
libguile/foreign.c | 4 +-
libguile/foreign.h | 5 +-
libguile/frames.c | 62 +++---
libguile/frames.h | 76 ++++----
libguile/stacks.c | 7 +-
libguile/throw.c | 9 +-
libguile/vm-engine.c | 112 ++++++-----
libguile/vm.c | 394 +++++++++++++++++++--------------------
libguile/vm.h | 25 ++-
15 files changed, 400 insertions(+), 392 deletions(-)
diff --git a/libguile/continuations.c b/libguile/continuations.c
index 8dca62e2d..7cc3cea10 100644
--- a/libguile/continuations.c
+++ b/libguile/continuations.c
@@ -180,10 +180,13 @@ scm_i_continuation_to_frame (SCM continuation, struct scm_frame *frame)
if (scm_is_true (cont->vm_cont))
{
struct scm_vm_cont *data = SCM_VM_CONT_DATA (cont->vm_cont);
+ union scm_vm_stack_element *stack_top;
+ /* FIXME vm_cont should hold fp/sp offsets */
+ stack_top = data->stack_bottom + data->stack_size;
frame->stack_holder = data;
- frame->fp_offset = (data->fp + data->reloc) - data->stack_base;
- frame->sp_offset = (data->sp + data->reloc) - data->stack_base;
+ frame->fp_offset = stack_top - (data->fp + data->reloc);
+ frame->sp_offset = stack_top - (data->sp + data->reloc);
frame->ip = data->ra;
return 1;
diff --git a/libguile/control.c b/libguile/control.c
index 347d69715..a3457342b 100644
--- a/libguile/control.c
+++ b/libguile/control.c
@@ -39,19 +39,22 @@
/* Only to be called if the SCM_I_SETJMP returns 1 */
SCM
-scm_i_prompt_pop_abort_args_x (struct scm_vm *vp)
+scm_i_prompt_pop_abort_args_x (struct scm_vm *vp,
+ scm_t_ptrdiff saved_stack_depth)
{
size_t i, n;
+ scm_t_ptrdiff stack_depth;
SCM vals = SCM_EOL;
- n = scm_to_size_t (vp->sp[0]);
- for (i = 0; i < n; i++)
- vals = scm_cons (vp->sp[-(i + 1)], vals);
+ stack_depth = vp->stack_top - vp->sp;
+ if (stack_depth < saved_stack_depth)
+ abort ();
+ n = stack_depth - saved_stack_depth;
- /* The abort did reset the VM's registers, but then these values
- were pushed on; so we need to pop them ourselves. */
- vp->sp -= n + 1;
- /* FIXME NULLSTACK */
+ for (i = 0; i < n; i++)
+ vals = scm_cons (vp->sp[i].scm, vals);
+
+ vp->sp += n;
return vals;
}
@@ -79,8 +82,8 @@ make_partial_continuation (SCM vm_cont)
static SCM
reify_partial_continuation (struct scm_vm *vp,
- SCM *saved_fp,
- SCM *saved_sp,
+ union scm_vm_stack_element *saved_fp,
+ union scm_vm_stack_element *saved_sp,
scm_t_uint32 *saved_ip,
scm_i_jmp_buf *saved_registers,
scm_t_dynstack *dynstack,
@@ -88,7 +91,7 @@ reify_partial_continuation (struct scm_vm *vp,
{
SCM vm_cont;
scm_t_uint32 flags;
- SCM *bottom_fp;
+ union scm_vm_stack_element *base_fp;
flags = SCM_F_VM_CONT_PARTIAL;
/* If we are aborting to a prompt that has the same registers as those
@@ -98,24 +101,20 @@ reify_partial_continuation (struct scm_vm *vp,
if (saved_registers && saved_registers == current_registers)
flags |= SCM_F_VM_CONT_REWINDABLE;
- /* Walk the stack down until we find the first frame after saved_fp.
- We will save the stack down to that frame. It used to be that we
- could determine the stack bottom in O(1) time, but that's no longer
+ /* Walk the stack until we find the first frame newer than saved_fp.
+ We will save the stack until that frame. It used to be that we
+ could determine the stack base in O(1) time, but that's no longer
the case, since the thunk application doesn't occur where the
prompt is saved. */
- for (bottom_fp = vp->fp;
- SCM_FRAME_DYNAMIC_LINK (bottom_fp) > saved_fp;
- bottom_fp = SCM_FRAME_DYNAMIC_LINK (bottom_fp));
+ for (base_fp = vp->fp;
+ SCM_FRAME_DYNAMIC_LINK (base_fp) < saved_fp;
+ base_fp = SCM_FRAME_DYNAMIC_LINK (base_fp));
- if (SCM_FRAME_DYNAMIC_LINK (bottom_fp) != saved_fp)
+ if (SCM_FRAME_DYNAMIC_LINK (base_fp) != saved_fp)
abort();
- /* Capture from the top of the thunk application frame up to the end. */
- vm_cont = scm_i_vm_capture_stack (&SCM_FRAME_LOCAL (bottom_fp, 0),
- vp->fp,
- vp->sp,
- vp->ip,
- dynstack,
+ /* Capture from the base_fp to the top thunk application frame. */
+ vm_cont = scm_i_vm_capture_stack (base_fp, vp->fp, vp->sp, vp->ip, dynstack,
flags);
return make_partial_continuation (vm_cont);
@@ -130,7 +129,7 @@ scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM *argv,
scm_t_bits *prompt;
scm_t_dynstack_prompt_flags flags;
scm_t_ptrdiff fp_offset, sp_offset;
- SCM *fp, *sp;
+ union scm_vm_stack_element *fp, *sp;
scm_t_uint32 *ip;
scm_i_jmp_buf *registers;
size_t i;
@@ -142,8 +141,8 @@ scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM *argv,
if (!prompt)
scm_misc_error ("abort", "Abort to unknown prompt", scm_list_1 (tag));
- fp = vp->stack_base + fp_offset;
- sp = vp->stack_base + sp_offset;
+ fp = vp->stack_top - fp_offset;
+ sp = vp->stack_top - sp_offset;
/* Only reify if the continuation referenced in the handler. */
if (flags & SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY)
@@ -162,19 +161,17 @@ scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM *argv,
/* Restore VM regs */
vp->fp = fp;
- vp->sp = sp;
+ vp->sp = sp - n - 1;
vp->ip = ip;
/* Since we're jumping down, we should always have enough space. */
- if (vp->sp + n + 1 >= vp->stack_limit)
+ if (vp->sp < vp->stack_limit)
abort ();
/* Push vals */
- *(++(vp->sp)) = cont;
+ vp->sp[n].scm = cont;
for (i = 0; i < n; i++)
- *(++(vp->sp)) = argv[i];
- if (flags & SCM_F_DYNSTACK_PROMPT_PUSH_NARGS)
- *(++(vp->sp)) = scm_from_size_t (n+1); /* +1 for continuation */
+ vp->sp[n - i - 1].scm = argv[i];
/* Jump! */
SCM_I_LONGJMP (*registers, 1);
diff --git a/libguile/control.h b/libguile/control.h
index 4b76591aa..84990ab10 100644
--- a/libguile/control.h
+++ b/libguile/control.h
@@ -22,7 +22,8 @@
#include "libguile/vm.h"
-SCM_INTERNAL SCM scm_i_prompt_pop_abort_args_x (struct scm_vm *vp);
+SCM_INTERNAL SCM scm_i_prompt_pop_abort_args_x (struct scm_vm *vp,
+ scm_t_ptrdiff saved_stack_depth);
SCM_INTERNAL void scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM *argv,
scm_i_jmp_buf *registers) SCM_NORETURN;
diff --git a/libguile/dynstack.c b/libguile/dynstack.c
index 9235ec495..bda1a16b5 100644
--- a/libguile/dynstack.c
+++ b/libguile/dynstack.c
@@ -484,8 +484,8 @@ scm_dynstack_wind_prompt (scm_t_dynstack *dynstack, scm_t_bits *item,
scm_dynstack_push_prompt (dynstack,
SCM_DYNSTACK_TAG_FLAGS (tag),
PROMPT_KEY (item),
- PROMPT_FP (item) + reloc,
- PROMPT_SP (item) + reloc,
+ PROMPT_FP (item) - reloc,
+ PROMPT_SP (item) - reloc,
PROMPT_IP (item),
registers);
}
diff --git a/libguile/dynstack.h b/libguile/dynstack.h
index 7b31acedf..853f0684d 100644
--- a/libguile/dynstack.h
+++ b/libguile/dynstack.h
@@ -129,8 +129,7 @@ typedef enum {
} scm_t_dynstack_winder_flags;
typedef enum {
- SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY = (1 << SCM_DYNSTACK_TAG_FLAGS_SHIFT),
- SCM_F_DYNSTACK_PROMPT_PUSH_NARGS = (2 << SCM_DYNSTACK_TAG_FLAGS_SHIFT)
+ SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY = (1 << SCM_DYNSTACK_TAG_FLAGS_SHIFT)
} scm_t_dynstack_prompt_flags;
typedef void (*scm_t_guard) (void *);
diff --git a/libguile/eval.c b/libguile/eval.c
index 735e6c0b3..09fa71df4 100644
--- a/libguile/eval.c
+++ b/libguile/eval.c
@@ -424,23 +424,22 @@ eval (SCM x, SCM env)
case SCM_M_CALL_WITH_PROMPT:
{
struct scm_vm *vp;
- SCM k, res;
+ SCM k, handler, res;
scm_i_jmp_buf registers;
- /* We need the handler after nonlocal return to the setjmp, so
- make sure it is volatile. */
- volatile SCM handler;
+ scm_t_ptrdiff saved_stack_depth;
k = EVAL1 (CAR (mx), env);
handler = EVAL1 (CDDR (mx), env);
vp = scm_the_vm ();
+ saved_stack_depth = vp->stack_top - vp->sp;
+
/* Push the prompt onto the dynamic stack. */
scm_dynstack_push_prompt (&SCM_I_CURRENT_THREAD->dynstack,
- SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY
- | SCM_F_DYNSTACK_PROMPT_PUSH_NARGS,
+ SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY,
k,
- vp->fp - vp->stack_base,
- vp->sp - vp->stack_base,
+ vp->stack_top - vp->fp,
+ saved_stack_depth,
vp->ip,
®isters);
@@ -449,8 +448,7 @@ eval (SCM x, SCM env)
/* The prompt exited nonlocally. */
scm_gc_after_nonlocal_exit ();
proc = handler;
- vp = scm_the_vm ();
- args = scm_i_prompt_pop_abort_args_x (vp);
+ args = scm_i_prompt_pop_abort_args_x (vp, saved_stack_depth);
goto apply_proc;
}
diff --git a/libguile/foreign.c b/libguile/foreign.c
index 0cab6b8b0..3ac06591d 100644
--- a/libguile/foreign.c
+++ b/libguile/foreign.c
@@ -977,7 +977,7 @@ pack (const ffi_type * type, const void *loc, int return_value_p)
SCM
-scm_i_foreign_call (SCM foreign, const SCM *argv)
+scm_i_foreign_call (SCM foreign, const union scm_vm_stack_element *argv)
{
/* FOREIGN is the pair that cif_to_procedure set as the 0th element of the
objtable. */
@@ -1016,7 +1016,7 @@ scm_i_foreign_call (SCM foreign, const SCM *argv)
args[i] = (void *) ROUND_UP ((scm_t_uintptr) data + off,
cif->arg_types[i]->alignment);
assert ((scm_t_uintptr) args[i] % cif->arg_types[i]->alignment == 0);
- unpack (cif->arg_types[i], args[i], argv[i], 0);
+ unpack (cif->arg_types[i], args[i], argv[cif->nargs - i - 1].scm, 0);
}
/* Prepare space for the return value. On some platforms, such as
diff --git a/libguile/foreign.h b/libguile/foreign.h
index fbb97640b..53f39d5c7 100644
--- a/libguile/foreign.h
+++ b/libguile/foreign.h
@@ -93,11 +93,14 @@ SCM_INTERNAL SCM scm_pointer_to_string (SCM pointer, SCM length, SCM encoding);
arguments.
*/
+union scm_vm_stack_element;
+
SCM_API SCM scm_pointer_to_procedure (SCM return_type, SCM func_ptr,
SCM arg_types);
SCM_API SCM scm_procedure_to_pointer (SCM return_type, SCM func_ptr,
SCM arg_types);
-SCM_INTERNAL SCM scm_i_foreign_call (SCM foreign, const SCM *argv);
+SCM_INTERNAL SCM scm_i_foreign_call (SCM foreign,
+ const union scm_vm_stack_element *argv);
SCM_INTERNAL int scm_i_foreign_arity (SCM foreign,
int *req, int *opt, int *rest);
diff --git a/libguile/frames.c b/libguile/frames.c
index 2162f49ce..f89b0fd5b 100644
--- a/libguile/frames.c
+++ b/libguile/frames.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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
@@ -25,14 +25,6 @@
#include "_scm.h"
#include "frames.h"
#include "vm.h"
-#include
-
-/* Make sure assumptions on the layout of `struct scm_vm_frame' hold. */
-verify (sizeof (SCM) == sizeof (SCM *));
-verify (sizeof (struct scm_vm_frame) == 3 * sizeof (SCM));
-verify (offsetof (struct scm_vm_frame, dynamic_link) == 0);
-
-
SCM
scm_c_make_frame (enum scm_vm_frame_kind kind, const struct scm_frame *frame)
@@ -57,16 +49,19 @@ scm_i_frame_print (SCM frame, SCM port, scm_print_state *pstate)
scm_puts_unlocked (">", port);
}
-static SCM*
-frame_stack_base (enum scm_vm_frame_kind kind, const struct scm_frame *frame)
+static union scm_vm_stack_element*
+frame_stack_top (enum scm_vm_frame_kind kind, const struct scm_frame *frame)
{
switch (kind)
{
- case SCM_VM_FRAME_KIND_CONT:
- return ((struct scm_vm_cont *) frame->stack_holder)->stack_base;
+ case SCM_VM_FRAME_KIND_CONT:
+ {
+ struct scm_vm_cont *cont = frame->stack_holder;
+ return cont->stack_bottom + cont->stack_size;
+ }
case SCM_VM_FRAME_KIND_VM:
- return ((struct scm_vm *) frame->stack_holder)->stack_base;
+ return ((struct scm_vm *) frame->stack_holder)->stack_top;
default:
abort ();
@@ -89,14 +84,14 @@ frame_offset (enum scm_vm_frame_kind kind, const struct scm_frame *frame)
}
}
-SCM*
-scm_i_frame_stack_base (SCM frame)
-#define FUNC_NAME "frame-stack-base"
+union scm_vm_stack_element*
+scm_i_frame_stack_top (SCM frame)
+#define FUNC_NAME "frame-stack-top"
{
SCM_VALIDATE_VM_FRAME (1, frame);
- return frame_stack_base (SCM_VM_FRAME_KIND (frame),
- SCM_VM_FRAME_DATA (frame));
+ return frame_stack_top (SCM_VM_FRAME_KIND (frame),
+ SCM_VM_FRAME_DATA (frame));
}
#undef FUNC_NAME
@@ -130,10 +125,10 @@ SCM_DEFINE (scm_frame_p, "frame?", 1, 0, 0,
SCM
scm_c_frame_closure (enum scm_vm_frame_kind kind, const struct scm_frame *frame)
{
- SCM *fp, *sp;
+ union scm_vm_stack_element *fp, *sp;
- fp = frame_stack_base (kind, frame) + frame->fp_offset;
- sp = frame_stack_base (kind, frame) + frame->sp_offset;
+ fp = frame_stack_top (kind, frame) - frame->fp_offset;
+ sp = frame_stack_top (kind, frame) - frame->sp_offset;
if (SCM_FRAME_NUM_LOCALS (fp, sp) > 0)
return SCM_FRAME_LOCAL (fp, 0);
@@ -214,7 +209,7 @@ SCM_DEFINE (scm_frame_num_locals, "frame-num-locals", 1, 0, 0,
"")
#define FUNC_NAME s_scm_frame_num_locals
{
- SCM *fp, *sp;
+ union scm_vm_stack_element *fp, *sp;
SCM_VALIDATE_VM_FRAME (1, frame);
@@ -230,7 +225,7 @@ SCM_DEFINE (scm_frame_local_ref, "frame-local-ref", 2, 0, 0,
"")
#define FUNC_NAME s_scm_frame_local_ref
{
- SCM *fp, *sp;
+ union scm_vm_stack_element *fp, *sp;
unsigned int i;
SCM_VALIDATE_VM_FRAME (1, frame);
@@ -252,7 +247,7 @@ SCM_DEFINE (scm_frame_local_set_x, "frame-local-set!", 3, 0, 0,
"")
#define FUNC_NAME s_scm_frame_local_set_x
{
- SCM *fp, *sp;
+ union scm_vm_stack_element *fp, *sp;
unsigned int i;
SCM_VALIDATE_VM_FRAME (1, frame);
@@ -314,8 +309,7 @@ SCM_DEFINE (scm_frame_return_address, "frame-return-address", 1, 0, 0,
}
#undef FUNC_NAME
-#define RELOC(kind, frame, val) \
- (((SCM *) (val)) + frame_offset (kind, frame))
+#define RELOC(kind, frame, val) ((val) + frame_offset (kind, frame))
SCM_DEFINE (scm_frame_dynamic_link, "frame-dynamic-link", 1, 0, 0,
(SCM frame),
@@ -334,13 +328,13 @@ SCM_DEFINE (scm_frame_dynamic_link, "frame-dynamic-link", 1, 0, 0,
int
scm_c_frame_previous (enum scm_vm_frame_kind kind, struct scm_frame *frame)
{
- SCM *this_fp, *new_fp, *new_sp;
- SCM *stack_base = frame_stack_base (kind, frame);
+ union scm_vm_stack_element *this_fp, *new_fp, *new_sp;
+ union scm_vm_stack_element *stack_top = frame_stack_top (kind, frame);
again:
- this_fp = frame->fp_offset + stack_base;
+ this_fp = stack_top - frame->fp_offset;
- if (this_fp == stack_base)
+ if (this_fp == stack_top)
return 0;
new_fp = SCM_FRAME_DYNAMIC_LINK (this_fp);
@@ -350,12 +344,12 @@ scm_c_frame_previous (enum scm_vm_frame_kind kind, struct scm_frame *frame)
new_fp = RELOC (kind, frame, new_fp);
- if (new_fp < stack_base)
+ if (new_fp > stack_top)
return 0;
new_sp = SCM_FRAME_PREVIOUS_SP (this_fp);
- frame->fp_offset = new_fp - stack_base;
- frame->sp_offset = new_sp - stack_base;
+ frame->fp_offset = stack_top - new_fp;
+ frame->sp_offset = stack_top - new_sp;
frame->ip = SCM_FRAME_RETURN_ADDRESS (this_fp);
{
diff --git a/libguile/frames.h b/libguile/frames.h
index 31f86345f..c2f1e57db 100644
--- a/libguile/frames.h
+++ b/libguile/frames.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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
@@ -38,24 +38,29 @@
Stack frame layout
------------------
- /------------------\
- | Local N-1 | <- sp
| ... |
- | Local 1 |
- | Local 0 | <- fp = SCM_FRAME_LOCALS_ADDRESS (fp)
- +==================+
+ +==================+ <- fp + 2 = SCM_FRAME_PREVIOUS_SP (fp)
+ | Dynamic link |
+ +------------------+
| Return address |
- | Dynamic link | <- fp - 2 = SCM_FRAME_LOWER_ADDRESS (fp)
- +==================+
- | | <- fp - 3 = SCM_FRAME_PREVIOUS_SP (fp)
+ +==================+ <- fp
+ | Local 0 |
+ +------------------+
+ | Local 1 |
+ +------------------+
+ | ... |
+ +------------------+
+ | Local N-1 |
+ \------------------/ <- sp
+
+ The stack grows down.
The calling convention is that a caller prepares a stack frame
consisting of the saved FP and the return address, followed by the
procedure and then the arguments to the call, in order. Thus in the
beginning of a call, the procedure being called is in slot 0, the
first argument is in slot 1, and the SP points to the last argument.
- The number of arguments, including the procedure, is thus SP - FP +
- 1.
+ The number of arguments, including the procedure, is thus FP - SP.
After ensuring that the correct number of arguments have been passed,
a function will set the stack pointer to point to the last local
@@ -80,35 +85,26 @@
-/* This structure maps to the contents of a VM stack frame. It can
- alias a frame directly. */
-struct scm_vm_frame
+/* Each element on the stack occupies the same amount of space. */
+union scm_vm_stack_element
{
- SCM *dynamic_link;
- scm_t_uint32 *return_address;
- SCM locals[1]; /* Variable-length */
+ union scm_vm_stack_element *fp;
+ scm_t_uint32 *ip;
+ SCM scm;
+
+ /* For GC purposes. */
+ void *ptr;
+ scm_t_bits bits;
};
-#define SCM_FRAME_LOWER_ADDRESS(fp) (((SCM *) (fp)) - 2)
-#define SCM_FRAME_STRUCT(fp) \
- ((struct scm_vm_frame *) SCM_FRAME_LOWER_ADDRESS (fp))
-#define SCM_FRAME_LOCALS_ADDRESS(fp) (SCM_FRAME_STRUCT (fp)->locals)
-
-#define SCM_FRAME_PREVIOUS_SP(fp) (((SCM *) (fp)) - 3)
-
-#define SCM_FRAME_RETURN_ADDRESS(fp) \
- (SCM_FRAME_STRUCT (fp)->return_address)
-#define SCM_FRAME_SET_RETURN_ADDRESS(fp, ra) \
- SCM_FRAME_STRUCT (fp)->return_address = (ra)
-#define SCM_FRAME_DYNAMIC_LINK(fp) \
- (SCM_FRAME_STRUCT (fp)->dynamic_link)
-#define SCM_FRAME_SET_DYNAMIC_LINK(fp, dl) \
- SCM_FRAME_DYNAMIC_LINK (fp) = (dl)
-#define SCM_FRAME_LOCAL(fp,i) \
- (SCM_FRAME_STRUCT (fp)->locals[i])
-
-#define SCM_FRAME_NUM_LOCALS(fp, sp) \
- ((sp) + 1 - &SCM_FRAME_LOCAL (fp, 0))
+#define SCM_FRAME_PREVIOUS_SP(fp_) ((fp_) + 2)
+#define SCM_FRAME_RETURN_ADDRESS(fp_) ((fp_)[0].ip)
+#define SCM_FRAME_SET_RETURN_ADDRESS(fp_, ra) ((fp_)[0].ip = (ra))
+#define SCM_FRAME_DYNAMIC_LINK(fp_) ((fp_)[1].fp)
+#define SCM_FRAME_SET_DYNAMIC_LINK(fp_, dl) ((fp_)[1].fp = (dl))
+#define SCM_FRAME_SLOT(fp_,i) ((fp_) - (i) - 1)
+#define SCM_FRAME_LOCAL(fp_,i) (SCM_FRAME_SLOT (fp_, i)->scm)
+#define SCM_FRAME_NUM_LOCALS(fp_, sp) ((fp_) - (sp))
/*
@@ -137,13 +133,13 @@ enum scm_vm_frame_kind
#define SCM_VM_FRAME_STACK_HOLDER(f) SCM_VM_FRAME_DATA (f)->stack_holder
#define SCM_VM_FRAME_FP_OFFSET(f) SCM_VM_FRAME_DATA (f)->fp_offset
#define SCM_VM_FRAME_SP_OFFSET(f) SCM_VM_FRAME_DATA (f)->sp_offset
-#define SCM_VM_FRAME_FP(f) (SCM_VM_FRAME_FP_OFFSET (f) + scm_i_frame_stack_base (f))
-#define SCM_VM_FRAME_SP(f) (SCM_VM_FRAME_SP_OFFSET (f) + scm_i_frame_stack_base (f))
+#define SCM_VM_FRAME_FP(f) (scm_i_frame_stack_top (f) - SCM_VM_FRAME_FP_OFFSET (f))
+#define SCM_VM_FRAME_SP(f) (scm_i_frame_stack_top (f) - SCM_VM_FRAME_SP_OFFSET (f))
#define SCM_VM_FRAME_IP(f) SCM_VM_FRAME_DATA (f)->ip
#define SCM_VM_FRAME_OFFSET(f) scm_i_frame_offset (f)
#define SCM_VALIDATE_VM_FRAME(p,x) SCM_MAKE_VALIDATE (p, x, VM_FRAME_P)
-SCM_INTERNAL SCM* scm_i_frame_stack_base (SCM frame);
+SCM_INTERNAL union scm_vm_stack_element* scm_i_frame_stack_top (SCM frame);
SCM_INTERNAL scm_t_ptrdiff scm_i_frame_offset (SCM frame);
/* See notes in frames.c before using this. */
diff --git a/libguile/stacks.c b/libguile/stacks.c
index a09c3b9a3..ec3ec789f 100644
--- a/libguile/stacks.c
+++ b/libguile/stacks.c
@@ -320,14 +320,17 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
{
SCM cont;
struct scm_vm_cont *c;
+ union scm_vm_stack_element *stack_top;
cont = scm_i_capture_current_stack ();
c = SCM_VM_CONT_DATA (cont);
+ /* FIXME vm_cont should hold fp/sp offsets */
+ stack_top = c->stack_bottom + c->stack_size;
kind = SCM_VM_FRAME_KIND_CONT;
frame.stack_holder = c;
- frame.fp_offset = (c->fp + c->reloc) - c->stack_base;
- frame.sp_offset = (c->sp + c->reloc) - c->stack_base;
+ frame.fp_offset = stack_top - (c->fp + c->reloc);
+ frame.sp_offset = stack_top - (c->sp + c->reloc);
frame.ip = c->ra;
}
else if (SCM_VM_FRAME_P (obj))
diff --git a/libguile/throw.c b/libguile/throw.c
index bbde5e009..773ac2783 100644
--- a/libguile/throw.c
+++ b/libguile/throw.c
@@ -102,14 +102,13 @@ catch (SCM tag, SCM thunk, SCM handler, SCM pre_unwind_handler)
scm_c_vector_set_x (eh, 3, pre_unwind_handler);
vp = scm_the_vm ();
- saved_stack_depth = vp->sp - vp->stack_base;
+ saved_stack_depth = vp->stack_top - vp->sp;
/* Push the prompt and exception handler onto the dynamic stack. */
scm_dynstack_push_prompt (dynstack,
- SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY
- | SCM_F_DYNSTACK_PROMPT_PUSH_NARGS,
+ SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY,
prompt_tag,
- vp->fp - vp->stack_base,
+ vp->stack_top - vp->fp,
saved_stack_depth,
vp->ip,
®isters);
@@ -125,7 +124,7 @@ catch (SCM tag, SCM thunk, SCM handler, SCM pre_unwind_handler)
/* FIXME: We know where the args will be on the stack; we could
avoid consing them. */
- args = scm_i_prompt_pop_abort_args_x (vp);
+ args = scm_i_prompt_pop_abort_args_x (vp, saved_stack_depth);
/* Cdr past the continuation. */
args = scm_cdr (args);
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 7e752dd14..f6cb0c49c 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -134,10 +134,10 @@
/* Virtual Machine
The VM has three state bits: the instruction pointer (IP), the frame
- pointer (FP), and the top-of-stack pointer (SP). We cache the first
- two of these in machine registers, local to the VM, because they are
- used extensively by the VM. As the SP is used more by code outside
- the VM than by the VM itself, we don't bother caching it locally.
+ pointer (FP), and the stack pointer (SP). We cache the first two of
+ these in machine registers, local to the VM, because they are used
+ extensively by the VM. As the SP is used more by code outside the VM
+ than by the VM itself, we don't bother caching it locally.
Since the FP changes infrequently, relative to the IP, we keep vp->fp
in sync with the local FP. This would be a big lose for the IP,
@@ -172,17 +172,17 @@
FP is valid across an ALLOC_FRAME call. Be careful! */
#define ALLOC_FRAME(n) \
do { \
- SCM *new_sp = LOCAL_ADDRESS (n - 1); \
- if (new_sp > vp->sp_max_since_gc) \
+ union scm_vm_stack_element *new_sp = LOCAL_ADDRESS (n - 1); \
+ if (new_sp < vp->sp_min_since_gc) \
{ \
- if (SCM_UNLIKELY (new_sp >= vp->stack_limit)) \
+ if (SCM_UNLIKELY (new_sp < vp->stack_limit)) \
{ \
SYNC_IP (); \
vm_expand_stack (vp, new_sp); \
CACHE_FP (); \
} \
else \
- vp->sp_max_since_gc = vp->sp = new_sp; \
+ vp->sp_min_since_gc = vp->sp = new_sp; \
} \
else \
vp->sp = new_sp; \
@@ -193,15 +193,15 @@
#define RESET_FRAME(n) \
do { \
vp->sp = LOCAL_ADDRESS (n - 1); \
- if (vp->sp > vp->sp_max_since_gc) \
- vp->sp_max_since_gc = vp->sp; \
+ if (vp->sp < vp->sp_min_since_gc) \
+ vp->sp_min_since_gc = vp->sp; \
} while (0)
/* Compute the number of locals in the frame. At a call, this is equal
to the number of actual arguments when a function is first called,
plus one for the function. */
#define FRAME_LOCALS_COUNT_FROM(slot) \
- (vp->sp + 1 - LOCAL_ADDRESS (slot))
+ (LOCAL_ADDRESS (slot) + 1 - vp->sp)
#define FRAME_LOCALS_COUNT() \
FRAME_LOCALS_COUNT_FROM (0)
@@ -246,7 +246,7 @@
case opcode:
#endif
-#define LOCAL_ADDRESS(i) (&SCM_FRAME_LOCAL (fp, i))
+#define LOCAL_ADDRESS(i) SCM_FRAME_SLOT (fp, i)
#define LOCAL_REF(i) SCM_FRAME_LOCAL (fp, i)
#define LOCAL_SET(i,o) SCM_FRAME_LOCAL (fp, i) = o
@@ -257,18 +257,18 @@
#define RETURN_ONE_VALUE(ret) \
do { \
SCM val = ret; \
- SCM *old_fp; \
+ union scm_vm_stack_element *old_fp; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (2); \
old_fp = fp; \
ip = SCM_FRAME_RETURN_ADDRESS (fp); \
fp = vp->fp = SCM_FRAME_DYNAMIC_LINK (fp); \
/* Clear frame. */ \
- old_fp[-1] = SCM_BOOL_F; \
- old_fp[-2] = SCM_BOOL_F; \
+ old_fp[0].scm = SCM_BOOL_F; \
+ old_fp[1].scm = SCM_BOOL_F; \
/* Leave proc. */ \
SCM_FRAME_LOCAL (old_fp, 1) = val; \
- vp->sp = &SCM_FRAME_LOCAL (old_fp, 1); \
+ vp->sp = SCM_FRAME_SLOT (old_fp, 1); \
POP_CONTINUATION_HOOK (old_fp); \
NEXT (0); \
} while (0)
@@ -279,10 +279,10 @@
do { \
SCM vals = vals_; \
VM_HANDLE_INTERRUPTS; \
- ALLOC_FRAME (3); \
- fp[0] = vm_builtin_apply; \
- fp[1] = vm_builtin_values; \
- fp[2] = vals; \
+ ALLOC_FRAME (3); \
+ SCM_FRAME_LOCAL (fp, 0) = vm_builtin_apply; \
+ SCM_FRAME_LOCAL (fp, 1) = vm_builtin_values; \
+ SCM_FRAME_LOCAL (fp, 2) = vals; \
ip = (scm_t_uint32 *) vm_builtin_apply_code; \
goto op_tail_apply; \
} while (0)
@@ -429,7 +429,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
/* Frame pointer: A pointer into the stack, off of which we index
arguments and local variables. Pushed at function calls, popped on
returns. */
- register SCM *fp FP_REG;
+ register union scm_vm_stack_element *fp FP_REG;
/* Current opcode: A cache of *ip. */
register scm_t_uint32 op;
@@ -472,8 +472,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint32 n = FRAME_LOCALS_COUNT();
- /* Shuffle args up. */
- RESET_FRAME (n + 1);
+ /* Shuffle args up. (FIXME: no real need to shuffle; just set
+ IP and go. ) */
+ ALLOC_FRAME (n + 1);
while (n--)
LOCAL_SET (n + 1, LOCAL_REF (n));
@@ -546,7 +547,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
VM_DEFINE_OP (1, call, "call", OP2 (U8_U24, X8_U24))
{
scm_t_uint32 proc, nlocals;
- SCM *old_fp;
+ union scm_vm_stack_element *old_fp;
UNPACK_24 (op, proc);
UNPACK_24 (ip[1], nlocals);
@@ -556,7 +557,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
PUSH_CONTINUATION_HOOK ();
old_fp = fp;
- fp = vp->fp = old_fp + proc;
+ fp = vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
SCM_FRAME_SET_DYNAMIC_LINK (fp, old_fp);
SCM_FRAME_SET_RETURN_ADDRESS (fp, ip + 2);
@@ -586,7 +587,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint32 proc, nlocals;
scm_t_int32 label;
- SCM *old_fp;
+ union scm_vm_stack_element *old_fp;
UNPACK_24 (op, proc);
UNPACK_24 (ip[1], nlocals);
@@ -597,7 +598,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
PUSH_CONTINUATION_HOOK ();
old_fp = fp;
- fp = vp->fp = old_fp + proc;
+ fp = vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
SCM_FRAME_SET_DYNAMIC_LINK (fp, old_fp);
SCM_FRAME_SET_RETURN_ADDRESS (fp, ip + 3);
@@ -754,7 +755,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*/
VM_DEFINE_OP (9, return_values, "return-values", OP1 (U8_X24))
{
- SCM *old_fp;
+ union scm_vm_stack_element *old_fp;
VM_HANDLE_INTERRUPTS;
@@ -763,8 +764,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
fp = vp->fp = SCM_FRAME_DYNAMIC_LINK (fp);
/* Clear stack frame. */
- old_fp[-1] = SCM_BOOL_F;
- old_fp[-2] = SCM_BOOL_F;
+ old_fp[0].scm = SCM_BOOL_F;
+ old_fp[1].scm = SCM_BOOL_F;
POP_CONTINUATION_HOOK (old_fp);
@@ -804,34 +805,46 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ret = subr ();
break;
case 1:
- ret = subr (fp[1]);
+ ret = subr (LOCAL_REF (1));
break;
case 2:
- ret = subr (fp[1], fp[2]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2));
break;
case 3:
- ret = subr (fp[1], fp[2], fp[3]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3));
break;
case 4:
- ret = subr (fp[1], fp[2], fp[3], fp[4]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4));
break;
case 5:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5));
break;
case 6:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5], fp[6]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6));
break;
case 7:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5], fp[6], fp[7]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
+ LOCAL_REF (7));
break;
case 8:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5], fp[6], fp[7], fp[8]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
+ LOCAL_REF (7), LOCAL_REF (8));
break;
case 9:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5], fp[6], fp[7], fp[8], fp[9]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
+ LOCAL_REF (7), LOCAL_REF (8), LOCAL_REF (9));
break;
case 10:
- ret = subr (fp[1], fp[2], fp[3], fp[4], fp[5], fp[6], fp[7], fp[8], fp[9], fp[10]);
+ ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
+ LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
+ LOCAL_REF (7), LOCAL_REF (8), LOCAL_REF (9),
+ LOCAL_REF (10));
break;
default:
abort ();
@@ -869,7 +882,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
// FIXME: separate args
ret = scm_i_foreign_call (scm_inline_cons (thread, cif, pointer),
- LOCAL_ADDRESS (1));
+ vp->sp);
CACHE_FP ();
@@ -903,7 +916,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
vm_return_to_continuation (scm_i_contregs_vp (contregs),
scm_i_contregs_vm_cont (contregs),
FRAME_LOCALS_COUNT_FROM (1),
- LOCAL_ADDRESS (1));
+ vp->sp);
scm_i_reinstate_continuation (contregs);
/* no NEXT */
@@ -912,7 +925,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
/* compose-continuation cont:24
*
- * Compose a partial continution with the current continuation. The
+ * Compose a partial continuation with the current continuation. The
* arguments to the continuation are taken from the stack. CONT is a
* free variable containing the reified continuation. This
* instruction is part of the implementation of partial continuations,
@@ -930,9 +943,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
VM_ASSERT (SCM_VM_CONT_REWINDABLE_P (vmcont),
vm_error_continuation_not_rewindable (vmcont));
vm_reinstate_partial_continuation (vp, vmcont, FRAME_LOCALS_COUNT_FROM (1),
- LOCAL_ADDRESS (1),
- &thread->dynstack,
- registers);
+ &thread->dynstack, registers);
CACHE_REGISTER ();
NEXT (0);
}
@@ -999,7 +1010,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SYNC_IP ();
dynstack = scm_dynstack_capture_all (&thread->dynstack);
- vm_cont = scm_i_vm_capture_stack (vp->stack_base,
+ vm_cont = scm_i_vm_capture_stack (vp->stack_top,
SCM_FRAME_DYNAMIC_LINK (fp),
SCM_FRAME_PREVIOUS_SP (fp),
SCM_FRAME_RETURN_ADDRESS (fp),
@@ -1051,8 +1062,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
it continues with the next instruction. */
ip++;
SYNC_IP ();
- vm_abort (vp, LOCAL_REF (1), nlocals - 2, LOCAL_ADDRESS (2),
- SCM_EOL, LOCAL_ADDRESS (0), registers);
+ vm_abort (vp, LOCAL_REF (1), nlocals - 2, registers);
/* vm_abort should not return */
abort ();
@@ -2065,8 +2075,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
flags = escape_only_p ? SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY : 0;
scm_dynstack_push_prompt (&thread->dynstack, flags,
LOCAL_REF (tag),
- fp - vp->stack_base,
- LOCAL_ADDRESS (proc_slot) - vp->stack_base,
+ vp->stack_top - fp,
+ vp->stack_top - LOCAL_ADDRESS (proc_slot),
ip + offset,
registers);
NEXT (3);
diff --git a/libguile/vm.c b/libguile/vm.c
index 0e5983575..d5a72727f 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -16,9 +16,6 @@
* 02110-1301 USA
*/
-/* For mremap(2) on GNU/Linux systems. */
-#define _GNU_SOURCE
-
#if HAVE_CONFIG_H
# include
#endif
@@ -65,7 +62,8 @@ static size_t page_size;
necessary, but might be if you think you found a bug in the VM. */
/* #define VM_ENABLE_ASSERTIONS */
-static void vm_expand_stack (struct scm_vm *vp, SCM *new_sp) SCM_NOINLINE;
+static void vm_expand_stack (struct scm_vm *vp,
+ union scm_vm_stack_element *new_sp) SCM_NOINLINE;
/* RESTORE is for the case where we know we have done a PUSH of equal or
greater stack size in the past. Otherwise PUSH is the thing, which
@@ -73,28 +71,29 @@ static void vm_expand_stack (struct scm_vm *vp, SCM *new_sp) SCM_NOINLINE;
enum vm_increase_sp_kind { VM_SP_PUSH, VM_SP_RESTORE };
static inline void
-vm_increase_sp (struct scm_vm *vp, SCM *new_sp, enum vm_increase_sp_kind kind)
+vm_increase_sp (struct scm_vm *vp, union scm_vm_stack_element *new_sp,
+ enum vm_increase_sp_kind kind)
{
- if (new_sp <= vp->sp_max_since_gc)
+ if (new_sp >= vp->sp_min_since_gc)
{
vp->sp = new_sp;
return;
}
- if (kind == VM_SP_PUSH && new_sp >= vp->stack_limit)
+ if (kind == VM_SP_PUSH && new_sp < vp->stack_limit)
vm_expand_stack (vp, new_sp);
else
- vp->sp_max_since_gc = vp->sp = new_sp;
+ vp->sp_min_since_gc = vp->sp = new_sp;
}
static inline void
-vm_push_sp (struct scm_vm *vp, SCM *new_sp)
+vm_push_sp (struct scm_vm *vp, union scm_vm_stack_element *new_sp)
{
vm_increase_sp (vp, new_sp, VM_SP_PUSH);
}
static inline void
-vm_restore_sp (struct scm_vm *vp, SCM *new_sp)
+vm_restore_sp (struct scm_vm *vp, union scm_vm_stack_element *new_sp)
{
vm_increase_sp (vp, new_sp, VM_SP_RESTORE);
}
@@ -116,10 +115,12 @@ int
scm_i_vm_cont_to_frame (SCM cont, struct scm_frame *frame)
{
struct scm_vm_cont *data = SCM_VM_CONT_DATA (cont);
+ union scm_vm_stack_element *stack_top;
+ stack_top = data->stack_bottom + data->stack_size;
frame->stack_holder = data;
- frame->fp_offset = (data->fp + data->reloc) - data->stack_base;
- frame->sp_offset = (data->sp + data->reloc) - data->stack_base;
+ frame->fp_offset = stack_top - (data->fp + data->reloc);
+ frame->sp_offset = stack_top - (data->sp + data->reloc);
frame->ip = data->ra;
return 1;
@@ -129,23 +130,25 @@ scm_i_vm_cont_to_frame (SCM cont, struct scm_frame *frame)
is inside VM code, and call/cc was invoked within that same call to
vm_run. That's currently not implemented. */
SCM
-scm_i_vm_capture_stack (SCM *stack_base, SCM *fp, SCM *sp, scm_t_uint32 *ra,
+scm_i_vm_capture_stack (union scm_vm_stack_element *stack_top,
+ union scm_vm_stack_element *fp,
+ union scm_vm_stack_element *sp, scm_t_uint32 *ra,
scm_t_dynstack *dynstack, scm_t_uint32 flags)
{
struct scm_vm_cont *p;
p = scm_gc_malloc (sizeof (*p), "capture_vm_cont");
- p->stack_size = sp - stack_base + 1;
- p->stack_base = scm_gc_malloc (p->stack_size * sizeof (SCM),
- "capture_vm_cont");
+ p->stack_size = stack_top - sp;
+ p->stack_bottom = scm_gc_malloc (p->stack_size * sizeof (*p->stack_bottom),
+ "capture_vm_cont");
p->ra = ra;
p->sp = sp;
p->fp = fp;
- memcpy (p->stack_base, stack_base, (sp + 1 - stack_base) * sizeof (SCM));
- p->reloc = p->stack_base - stack_base;
+ memcpy (p->stack_bottom, sp, p->stack_size * sizeof (*p->stack_bottom));
+ p->reloc = (p->stack_bottom + p->stack_size) - stack_top;
p->dynstack = dynstack;
p->flags = flags;
- return scm_cell (scm_tc7_vm_cont, (scm_t_bits)p);
+ return scm_cell (scm_tc7_vm_cont, (scm_t_bits) p);
}
struct return_to_continuation_data
@@ -162,23 +165,27 @@ vm_return_to_continuation_inner (void *data_ptr)
struct return_to_continuation_data *data = data_ptr;
struct scm_vm *vp = data->vp;
struct scm_vm_cont *cp = data->cp;
+ union scm_vm_stack_element *cp_stack_top;
scm_t_ptrdiff reloc;
/* We know that there is enough space for the continuation, because we
captured it in the past. However there may have been an expansion
since the capture, so we may have to re-link the frame
pointers. */
- reloc = (vp->stack_base - (cp->stack_base - cp->reloc));
+ cp_stack_top = cp->stack_bottom + cp->stack_size;
+ reloc = (vp->stack_top - (cp_stack_top - cp->reloc));
vp->fp = cp->fp + reloc;
- memcpy (vp->stack_base, cp->stack_base, cp->stack_size * sizeof (SCM));
+ memcpy (vp->stack_top - cp->stack_size,
+ cp->stack_bottom,
+ cp->stack_size * sizeof (*cp->stack_bottom));
vm_restore_sp (vp, cp->sp + reloc);
if (reloc)
{
- SCM *fp = vp->fp;
+ union scm_vm_stack_element *fp = vp->fp;
while (fp)
{
- SCM *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
+ union scm_vm_stack_element *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
if (next_fp)
{
next_fp += reloc;
@@ -192,14 +199,15 @@ vm_return_to_continuation_inner (void *data_ptr)
}
static void
-vm_return_to_continuation (struct scm_vm *vp, SCM cont, size_t n, SCM *argv)
+vm_return_to_continuation (struct scm_vm *vp, SCM cont, size_t n,
+ union scm_vm_stack_element *argv)
{
struct scm_vm_cont *cp;
- SCM *argv_copy;
+ union scm_vm_stack_element *argv_copy;
struct return_to_continuation_data data;
- argv_copy = alloca (n * sizeof(SCM));
- memcpy (argv_copy, argv, n * sizeof(SCM));
+ argv_copy = alloca (n * sizeof (*argv));
+ memcpy (argv_copy, argv, n * sizeof (*argv));
cp = SCM_VM_CONT_DATA (cont);
@@ -208,22 +216,13 @@ vm_return_to_continuation (struct scm_vm *vp, SCM cont, size_t n, SCM *argv)
GC_call_with_alloc_lock (vm_return_to_continuation_inner, &data);
/* Now we have the continuation properly copied over. We just need to
- copy the arguments. It is not guaranteed that there is actually
- space for the arguments, though, so we have to bump the SP first. */
- vm_push_sp (vp, vp->sp + 3 + n);
-
- /* Now copy on an empty frame and the return values, as the
- continuation expects. */
- {
- SCM *base = vp->sp + 1 - 3 - n;
- size_t i;
-
- for (i = 0; i < 3; i++)
- base[i] = SCM_BOOL_F;
-
- for (i = 0; i < n; i++)
- base[i + 3] = argv_copy[i];
- }
+ copy on an empty frame and the return values, as the continuation
+ expects. */
+ vm_push_sp (vp, vp->sp - 3 - n);
+ vp->sp[n+2].scm = SCM_BOOL_F;
+ vp->sp[n+1].scm = SCM_BOOL_F;
+ vp->sp[n].scm = SCM_BOOL_F;
+ memcpy(vp->sp, argv_copy, n * sizeof (union scm_vm_stack_element));
vp->ip = cp->ra;
}
@@ -238,19 +237,21 @@ scm_i_capture_current_stack (void)
thread = SCM_I_CURRENT_THREAD;
vp = thread_vm (thread);
- return scm_i_vm_capture_stack (vp->stack_base, vp->fp, vp->sp, vp->ip,
+ return scm_i_vm_capture_stack (vp->stack_top, vp->fp, vp->sp, vp->ip,
scm_dynstack_capture_all (&thread->dynstack),
0);
}
static void vm_dispatch_apply_hook (struct scm_vm *vp) SCM_NOINLINE;
static void vm_dispatch_push_continuation_hook (struct scm_vm *vp) SCM_NOINLINE;
-static void vm_dispatch_pop_continuation_hook (struct scm_vm *vp, SCM *old_fp) SCM_NOINLINE;
+static void vm_dispatch_pop_continuation_hook
+ (struct scm_vm *vp, union scm_vm_stack_element *old_fp) SCM_NOINLINE;
static void vm_dispatch_next_hook (struct scm_vm *vp) SCM_NOINLINE;
static void vm_dispatch_abort_hook (struct scm_vm *vp) SCM_NOINLINE;
static void
-vm_dispatch_hook (struct scm_vm *vp, int hook_num, SCM *argv, int n)
+vm_dispatch_hook (struct scm_vm *vp, int hook_num,
+ union scm_vm_stack_element *argv, int n)
{
SCM hook;
struct scm_frame c_frame;
@@ -275,8 +276,8 @@ vm_dispatch_hook (struct scm_vm *vp, int hook_num, SCM *argv, int n)
seems reasonable to limit the lifetime of frame objects. */
c_frame.stack_holder = vp;
- c_frame.fp_offset = vp->fp - vp->stack_base;
- c_frame.sp_offset = vp->sp - vp->stack_base;
+ c_frame.fp_offset = vp->stack_top - vp->fp;
+ c_frame.sp_offset = vp->stack_top - vp->sp;
c_frame.ip = vp->ip;
/* Arrange for FRAME to be 8-byte aligned, like any other cell. */
@@ -298,15 +299,16 @@ vm_dispatch_hook (struct scm_vm *vp, int hook_num, SCM *argv, int n)
SCM args[2];
args[0] = SCM_PACK_POINTER (frame);
- args[1] = argv[0];
+ args[1] = argv[0].scm;
scm_c_run_hookn (hook, args, 2);
}
else
{
SCM args = SCM_EOL;
+ int i;
- while (n--)
- args = scm_cons (argv[n], args);
+ for (i = 0; i < n; i++)
+ args = scm_cons (argv[i].scm, args);
scm_c_run_hook (hook, scm_cons (SCM_PACK_POINTER (frame), args));
}
@@ -322,11 +324,11 @@ static void vm_dispatch_push_continuation_hook (struct scm_vm *vp)
{
return vm_dispatch_hook (vp, SCM_VM_PUSH_CONTINUATION_HOOK, NULL, 0);
}
-static void vm_dispatch_pop_continuation_hook (struct scm_vm *vp, SCM *old_fp)
+static void vm_dispatch_pop_continuation_hook (struct scm_vm *vp,
+ union scm_vm_stack_element *old_fp)
{
return vm_dispatch_hook (vp, SCM_VM_POP_CONTINUATION_HOOK,
- &SCM_FRAME_LOCAL (old_fp, 1),
- SCM_FRAME_NUM_LOCALS (old_fp, vp->sp) - 1);
+ vp->sp, SCM_FRAME_NUM_LOCALS (old_fp, vp->sp) - 1);
}
static void vm_dispatch_next_hook (struct scm_vm *vp)
{
@@ -335,38 +337,27 @@ static void vm_dispatch_next_hook (struct scm_vm *vp)
static void vm_dispatch_abort_hook (struct scm_vm *vp)
{
return vm_dispatch_hook (vp, SCM_VM_ABORT_CONTINUATION_HOOK,
- &SCM_FRAME_LOCAL (vp->fp, 1),
- SCM_FRAME_NUM_LOCALS (vp->fp, vp->sp) - 1);
+ vp->sp, SCM_FRAME_NUM_LOCALS (vp->fp, vp->sp) - 1);
}
static void
-vm_abort (struct scm_vm *vp, SCM tag,
- size_t nstack, SCM *stack_args, SCM tail, SCM *sp,
+vm_abort (struct scm_vm *vp, SCM tag, size_t nargs,
scm_i_jmp_buf *current_registers) SCM_NORETURN;
static void
-vm_abort (struct scm_vm *vp, SCM tag,
- size_t nstack, SCM *stack_args, SCM tail, SCM *sp,
+vm_abort (struct scm_vm *vp, SCM tag, size_t nargs,
scm_i_jmp_buf *current_registers)
{
size_t i;
- ssize_t tail_len;
SCM *argv;
- tail_len = scm_ilength (tail);
- if (tail_len < 0)
- scm_misc_error ("vm-engine", "tail values to abort should be a list",
- scm_list_1 (tail));
+ argv = alloca (nargs * sizeof (SCM));
+ for (i = 0; i < nargs; i++)
+ argv[i] = vp->sp[nargs - i - 1].scm;
- argv = alloca ((nstack + tail_len) * sizeof (SCM));
- for (i = 0; i < nstack; i++)
- argv[i] = stack_args[i];
- for (; i < nstack + tail_len; i++, tail = scm_cdr (tail))
- argv[i] = scm_car (tail);
+ vp->sp = vp->fp;
- vp->sp = sp;
-
- scm_c_abort (vp, tag, nstack + tail_len, argv, current_registers);
+ scm_c_abort (vp, tag, nargs, argv, current_registers);
}
struct vm_reinstate_partial_continuation_data
@@ -382,23 +373,23 @@ vm_reinstate_partial_continuation_inner (void *data_ptr)
struct vm_reinstate_partial_continuation_data *data = data_ptr;
struct scm_vm *vp = data->vp;
struct scm_vm_cont *cp = data->cp;
- SCM *base;
+ union scm_vm_stack_element *base_fp;
scm_t_ptrdiff reloc;
- base = SCM_FRAME_LOCALS_ADDRESS (vp->fp);
- reloc = cp->reloc + (base - cp->stack_base);
+ base_fp = vp->fp;
+ reloc = cp->reloc + (base_fp - (cp->stack_bottom + cp->stack_size));
- memcpy (base, cp->stack_base, cp->stack_size * sizeof (SCM));
+ memcpy (base_fp - cp->stack_size,
+ cp->stack_bottom,
+ cp->stack_size * sizeof (*cp->stack_bottom));
vp->fp = cp->fp + reloc;
vp->ip = cp->ra;
/* now relocate frame pointers */
{
- SCM *fp;
- for (fp = vp->fp;
- SCM_FRAME_LOWER_ADDRESS (fp) >= base;
- fp = SCM_FRAME_DYNAMIC_LINK (fp))
+ union scm_vm_stack_element *fp;
+ for (fp = vp->fp; fp < base_fp; fp = SCM_FRAME_DYNAMIC_LINK (fp))
SCM_FRAME_SET_DYNAMIC_LINK (fp, SCM_FRAME_DYNAMIC_LINK (fp) + reloc);
}
@@ -408,32 +399,32 @@ vm_reinstate_partial_continuation_inner (void *data_ptr)
}
static void
-vm_reinstate_partial_continuation (struct scm_vm *vp, SCM cont,
- size_t n, SCM *argv,
+vm_reinstate_partial_continuation (struct scm_vm *vp, SCM cont, size_t nargs,
scm_t_dynstack *dynstack,
scm_i_jmp_buf *registers)
{
struct vm_reinstate_partial_continuation_data data;
struct scm_vm_cont *cp;
- SCM *argv_copy;
+ union scm_vm_stack_element *args;
scm_t_ptrdiff reloc;
- size_t i;
- argv_copy = alloca (n * sizeof(SCM));
- memcpy (argv_copy, argv, n * sizeof(SCM));
+ args = alloca (nargs * sizeof (*args));
+ memcpy (args, vp->sp, nargs * sizeof (*args));
cp = SCM_VM_CONT_DATA (cont);
- vm_push_sp (vp, SCM_FRAME_LOCALS_ADDRESS (vp->fp) + cp->stack_size + n - 1);
+ vm_push_sp (vp, vp->fp - (cp->stack_size + nargs + 1));
data.vp = vp;
data.cp = cp;
GC_call_with_alloc_lock (vm_reinstate_partial_continuation_inner, &data);
reloc = data.reloc;
- /* Push the arguments. */
- for (i = 0; i < n; i++)
- vp->sp[i + 1 - n] = argv_copy[i];
+ /* The resume continuation will expect ARGS on the stack as if from a
+ multiple-value return. Fill in the closure slot with #f, and copy
+ the arguments into place. */
+ vp->sp[nargs].scm = SCM_BOOL_F;
+ memcpy (vp->sp, args, nargs * sizeof (*args));
/* The prompt captured a slice of the dynamic stack. Here we wind
those entries onto the current thread's stack. We also have to
@@ -789,20 +780,22 @@ typedef SCM (*scm_t_vm_engine) (scm_i_thread *current_thread, struct scm_vm *vp,
static const scm_t_vm_engine vm_engines[SCM_VM_NUM_ENGINES] =
{ vm_regular_engine, vm_debug_engine };
-static SCM*
+static union scm_vm_stack_element*
allocate_stack (size_t size)
-#define FUNC_NAME "make_vm"
{
void *ret;
- if (size >= ((size_t) -1) / sizeof (SCM))
+ if (size >= ((size_t) -1) / sizeof (union scm_vm_stack_element))
abort ();
- size *= sizeof (SCM);
+ size *= sizeof (union scm_vm_stack_element);
#if HAVE_SYS_MMAN_H
ret = mmap (NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ret == NULL)
+ /* Shouldn't happen. */
+ abort ();
if (ret == MAP_FAILED)
ret = NULL;
#else
@@ -810,19 +803,15 @@ allocate_stack (size_t size)
#endif
if (!ret)
- {
- perror ("allocate_stack failed");
- return NULL;
- }
+ perror ("allocate_stack failed");
- return (SCM *) ret;
+ return (union scm_vm_stack_element *) ret;
}
-#undef FUNC_NAME
static void
-free_stack (SCM *stack, size_t size)
+free_stack (union scm_vm_stack_element *stack, size_t size)
{
- size *= sizeof (SCM);
+ size *= sizeof (*stack);
#if HAVE_SYS_MMAN_H
munmap (stack, size);
@@ -831,36 +820,38 @@ free_stack (SCM *stack, size_t size)
#endif
}
-static SCM*
-expand_stack (SCM *old_stack, size_t old_size, size_t new_size)
+/* Ideally what we would like is an mremap or a realloc that grows at
+ the bottom, not the top. Oh well; mmap and memcpy are fast enough,
+ considering that they run very infrequently. */
+static union scm_vm_stack_element*
+expand_stack (union scm_vm_stack_element *old_bottom, size_t old_size,
+ size_t new_size)
#define FUNC_NAME "expand_stack"
{
-#if defined MREMAP_MAYMOVE
- void *new_stack;
+ union scm_vm_stack_element *new_bottom;
+ size_t extension_size;
- if (new_size >= ((size_t) -1) / sizeof (SCM))
+ if (new_size >= ((size_t) -1) / sizeof (union scm_vm_stack_element))
+ abort ();
+ if (new_size <= old_size)
abort ();
- old_size *= sizeof (SCM);
- new_size *= sizeof (SCM);
+ extension_size = new_size - old_size;
- new_stack = mremap (old_stack, old_size, new_size, MREMAP_MAYMOVE);
- if (new_stack == MAP_FAILED)
+ if ((size_t)old_bottom < extension_size * sizeof (union scm_vm_stack_element))
+ abort ();
+
+ new_bottom = allocate_stack (new_size);
+
+ if (!new_bottom)
return NULL;
- return (SCM *) new_stack;
-#else
- SCM *new_stack;
+ memcpy (new_bottom + extension_size,
+ old_bottom,
+ old_size * sizeof (union scm_vm_stack_element));
+ free_stack (old_bottom, old_size);
- new_stack = allocate_stack (new_size);
- if (!new_stack)
- return NULL;
-
- memcpy (new_stack, old_stack, old_size * sizeof (SCM));
- free_stack (old_stack, old_size);
-
- return new_stack;
-#endif
+ return new_bottom;
}
#undef FUNC_NAME
@@ -873,19 +864,21 @@ make_vm (void)
vp = scm_gc_malloc (sizeof (struct scm_vm), "vm");
- vp->stack_size = page_size / sizeof (SCM);
- vp->stack_base = allocate_stack (vp->stack_size);
- if (!vp->stack_base)
+ vp->stack_size = page_size / sizeof (union scm_vm_stack_element);
+ vp->stack_bottom = allocate_stack (vp->stack_size);
+ if (!vp->stack_bottom)
/* As in expand_stack, we don't have any way to throw an exception
if we can't allocate one measely page -- there's no stack to
handle it. For now, abort. */
abort ();
- vp->stack_limit = vp->stack_base + vp->stack_size;
+ vp->stack_top = vp->stack_bottom + vp->stack_size;
+ vp->stack_limit = vp->stack_bottom;
vp->overflow_handler_stack = SCM_EOL;
- vp->ip = NULL;
- vp->sp = vp->stack_base - 1;
- vp->fp = NULL;
- vp->engine = vm_default_engine;
+ vp->ip = NULL;
+ vp->sp = vp->stack_top;
+ vp->sp_min_since_gc = vp->sp;
+ vp->fp = NULL;
+ vp->engine = vm_default_engine;
vp->trace_level = 0;
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
vp->hooks[i] = SCM_BOOL_F;
@@ -898,30 +891,30 @@ static void
return_unused_stack_to_os (struct scm_vm *vp)
{
#if HAVE_SYS_MMAN_H
- scm_t_uintptr start = (scm_t_uintptr) (vp->sp + 1);
- scm_t_uintptr end = (scm_t_uintptr) vp->stack_limit;
+ scm_t_uintptr lo = (scm_t_uintptr) vp->stack_bottom;
+ scm_t_uintptr hi = (scm_t_uintptr) vp->sp;
/* The second condition is needed to protect against wrap-around. */
- if (vp->sp_max_since_gc < vp->stack_limit && vp->sp < vp->sp_max_since_gc)
- end = (scm_t_uintptr) (vp->sp_max_since_gc + 1);
+ if (vp->sp_min_since_gc >= vp->stack_bottom && vp->sp >= vp->sp_min_since_gc)
+ lo = (scm_t_uintptr) vp->sp_min_since_gc;
- start = ((start - 1U) | (page_size - 1U)) + 1U; /* round up */
- end = ((end - 1U) | (page_size - 1U)) + 1U; /* round up */
+ lo &= ~(page_size - 1U); /* round down */
+ hi &= ~(page_size - 1U); /* round down */
/* Return these pages to the OS. The next time they are paged in,
they will be zeroed. */
- if (start < end)
+ if (lo < hi)
{
int ret = 0;
do
- ret = madvise ((void *) start, end - start, MADV_DONTNEED);
+ ret = madvise ((void *) lo, hi - lo, MADV_DONTNEED);
while (ret && errno == -EAGAIN);
if (ret)
perror ("madvise failed");
}
- vp->sp_max_since_gc = vp->sp;
+ vp->sp_min_since_gc = vp->sp;
#endif
}
@@ -957,45 +950,44 @@ find_dead_slot_map (scm_t_uint32 *ip, struct dead_slot_map_cache *cache)
return map;
}
-/* Mark the VM stack region between its base and its current top. */
+/* Mark the active VM stack region. */
struct GC_ms_entry *
scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
struct GC_ms_entry *mark_stack_limit)
{
- SCM *sp, *fp;
+ union scm_vm_stack_element *sp, *fp;
/* The first frame will be marked conservatively (without a dead
slot map). This is because GC can happen at any point within the
hottest activation, due to multiple threads or per-instruction
hooks, and providing dead slot maps for all points in a program
would take a prohibitive amount of space. */
const scm_t_uint8 *dead_slots = NULL;
- scm_t_uintptr upper = (scm_t_uintptr) GC_greatest_plausible_heap_addr;
- scm_t_uintptr lower = (scm_t_uintptr) GC_least_plausible_heap_addr;
+ void *upper = (void *) GC_greatest_plausible_heap_addr;
+ void *lower = (void *) GC_least_plausible_heap_addr;
struct dead_slot_map_cache cache;
memset (&cache, 0, sizeof (cache));
for (fp = vp->fp, sp = vp->sp; fp; fp = SCM_FRAME_DYNAMIC_LINK (fp))
{
- for (; sp >= &SCM_FRAME_LOCAL (fp, 0); sp--)
+ scm_t_ptrdiff nlocals = SCM_FRAME_NUM_LOCALS (fp, sp);
+ size_t slot = nlocals - 1;
+ for (slot = nlocals - 1; sp < fp; sp++, slot--)
{
- SCM elt = *sp;
- if (SCM_NIMP (elt)
- && SCM_UNPACK (elt) >= lower && SCM_UNPACK (elt) <= upper)
+ if (SCM_NIMP (sp->scm) && sp->ptr >= lower && sp->ptr <= upper)
{
if (dead_slots)
{
- size_t slot = sp - &SCM_FRAME_LOCAL (fp, 0);
if (dead_slots[slot / 8U] & (1U << (slot % 8U)))
{
/* This value may become dead as a result of GC,
so we can't just leave it on the stack. */
- *sp = SCM_UNSPECIFIED;
+ sp->scm = SCM_UNSPECIFIED;
continue;
}
}
- mark_stack_ptr = GC_mark_and_push ((void *) elt,
+ mark_stack_ptr = GC_mark_and_push (sp->ptr,
mark_stack_ptr,
mark_stack_limit,
NULL);
@@ -1018,8 +1010,8 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
void
scm_i_vm_free_stack (struct scm_vm *vp)
{
- free_stack (vp->stack_base, vp->stack_size);
- vp->stack_base = vp->stack_limit = NULL;
+ free_stack (vp->stack_bottom, vp->stack_size);
+ vp->stack_bottom = vp->stack_top = vp->stack_limit = NULL;
vp->stack_size = 0;
}
@@ -1027,7 +1019,7 @@ struct vm_expand_stack_data
{
struct scm_vm *vp;
size_t stack_size;
- SCM *new_sp;
+ union scm_vm_stack_element *new_sp;
};
static void *
@@ -1036,34 +1028,35 @@ vm_expand_stack_inner (void *data_ptr)
struct vm_expand_stack_data *data = data_ptr;
struct scm_vm *vp = data->vp;
- SCM *old_stack, *new_stack;
+ union scm_vm_stack_element *old_top, *new_bottom;
size_t new_size;
scm_t_ptrdiff reloc;
+ old_top = vp->stack_top;
new_size = vp->stack_size;
while (new_size < data->stack_size)
new_size *= 2;
- old_stack = vp->stack_base;
- new_stack = expand_stack (vp->stack_base, vp->stack_size, new_size);
- if (!new_stack)
+ new_bottom = expand_stack (vp->stack_bottom, vp->stack_size, new_size);
+ if (!new_bottom)
return NULL;
- vp->stack_base = new_stack;
+ vp->stack_bottom = new_bottom;
vp->stack_size = new_size;
- vp->stack_limit = vp->stack_base + new_size;
- reloc = vp->stack_base - old_stack;
+ vp->stack_top = vp->stack_bottom + new_size;
+ vp->stack_limit = vp->stack_bottom;
+ reloc = vp->stack_top - old_top;
if (reloc)
{
- SCM *fp;
+ union scm_vm_stack_element *fp;
if (vp->fp)
vp->fp += reloc;
data->new_sp += reloc;
fp = vp->fp;
while (fp)
{
- SCM *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
+ union scm_vm_stack_element *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
if (next_fp)
{
next_fp += reloc;
@@ -1073,7 +1066,7 @@ vm_expand_stack_inner (void *data_ptr)
}
}
- return new_stack;
+ return new_bottom;
}
static scm_t_ptrdiff
@@ -1095,9 +1088,9 @@ static void
reset_stack_limit (struct scm_vm *vp)
{
if (should_handle_stack_overflow (vp, vp->stack_size))
- vp->stack_limit = vp->stack_base + current_overflow_size (vp);
+ vp->stack_limit = vp->stack_top - current_overflow_size (vp);
else
- vp->stack_limit = vp->stack_base + vp->stack_size;
+ vp->stack_limit = vp->stack_bottom;
}
struct overflow_handler_data
@@ -1127,9 +1120,9 @@ unwind_overflow_handler (void *ptr)
}
static void
-vm_expand_stack (struct scm_vm *vp, SCM *new_sp)
+vm_expand_stack (struct scm_vm *vp, union scm_vm_stack_element *new_sp)
{
- scm_t_ptrdiff stack_size = new_sp + 1 - vp->stack_base;
+ scm_t_ptrdiff stack_size = vp->stack_top - new_sp;
if (stack_size > vp->stack_size)
{
@@ -1146,7 +1139,7 @@ vm_expand_stack (struct scm_vm *vp, SCM *new_sp)
new_sp = data.new_sp;
}
- vp->sp_max_since_gc = vp->sp = new_sp;
+ vp->sp_min_since_gc = vp->sp = new_sp;
if (should_handle_stack_overflow (vp, stack_size))
{
@@ -1184,7 +1177,7 @@ vm_expand_stack (struct scm_vm *vp, SCM *new_sp)
scm_dynwind_end ();
- /* Recurse */
+ /* Recurse. */
return vm_expand_stack (vp, new_sp);
}
}
@@ -1209,10 +1202,13 @@ scm_call_n (SCM proc, SCM *argv, size_t nargs)
{
scm_i_thread *thread;
struct scm_vm *vp;
- SCM *base;
- ptrdiff_t base_frame_size;
- /* Cached variables. */
- scm_i_jmp_buf registers; /* used for prompts */
+ union scm_vm_stack_element *return_fp, *call_fp;
+ /* Since nargs can only describe the length of a valid argv array in
+ elements and each element is at least 4 bytes, nargs will not be
+ greater than INTMAX/2 and therefore we don't have to check for
+ overflow here or below. */
+ size_t return_nlocals = 1, call_nlocals = nargs + 1, frame_size = 2;
+ scm_t_ptrdiff stack_reserve_words;
size_t i;
thread = SCM_I_CURRENT_THREAD;
@@ -1220,32 +1216,36 @@ scm_call_n (SCM proc, SCM *argv, size_t nargs)
SCM_CHECK_STACK;
- /* Check that we have enough space: 3 words for the boot continuation,
- and 3 + nargs for the procedure application. */
- base_frame_size = 3 + 3 + nargs;
- vm_push_sp (vp, vp->sp + base_frame_size);
- base = vp->sp + 1 - base_frame_size;
+ /* It's not valid for argv to point into the stack already. */
+ if ((void *) argv < (void *) vp->stack_top &&
+ (void *) argv >= (void *) vp->sp)
+ abort();
- /* Since it's possible to receive the arguments on the stack itself,
- shuffle up the arguments first. */
- for (i = nargs; i > 0; i--)
- base[6 + i - 1] = argv[i - 1];
+ /* Check that we have enough space for the two stack frames: the
+ innermost one that makes the call, and its continuation which
+ receives the resulting value(s) and returns from the engine
+ call. */
+ stack_reserve_words = call_nlocals + frame_size + return_nlocals + frame_size;
+ vm_push_sp (vp, vp->sp - stack_reserve_words);
+
+ call_fp = vp->sp + call_nlocals;
+ return_fp = call_fp + frame_size + return_nlocals;
+
+ SCM_FRAME_SET_RETURN_ADDRESS (return_fp, vp->ip);
+ SCM_FRAME_SET_DYNAMIC_LINK (return_fp, vp->fp);
+ SCM_FRAME_LOCAL (return_fp, 0) = vm_boot_continuation;
- /* Push the boot continuation, which calls PROC and returns its
- result(s). */
- base[0] = SCM_PACK (vp->fp); /* dynamic link */
- base[1] = SCM_PACK (vp->ip); /* ra */
- base[2] = vm_boot_continuation;
- vp->fp = &base[2];
vp->ip = (scm_t_uint32 *) vm_boot_continuation_code;
+ vp->fp = call_fp;
- /* The pending call to PROC. */
- base[3] = SCM_PACK (vp->fp); /* dynamic link */
- base[4] = SCM_PACK (vp->ip); /* ra */
- base[5] = proc;
- vp->fp = &base[5];
+ SCM_FRAME_SET_RETURN_ADDRESS (call_fp, vp->ip);
+ SCM_FRAME_SET_DYNAMIC_LINK (call_fp, return_fp);
+ SCM_FRAME_LOCAL (call_fp, 0) = proc;
+ for (i = 0; i < nargs; i++)
+ SCM_FRAME_LOCAL (call_fp, i + 1) = argv[i];
{
+ scm_i_jmp_buf registers;
int resume = SCM_I_SETJMP (registers);
if (SCM_UNLIKELY (resume))
@@ -1449,7 +1449,7 @@ SCM_DEFINE (scm_call_with_stack_overflow_handler,
SCM new_limit, ret;
vp = scm_the_vm ();
- stack_size = vp->sp - vp->stack_base;
+ stack_size = vp->stack_top - vp->sp;
c_limit = scm_to_ptrdiff_t (limit);
if (c_limit <= 0)
@@ -1474,7 +1474,7 @@ SCM_DEFINE (scm_call_with_stack_overflow_handler,
scm_dynwind_unwind_handler (unwind_overflow_handler, &data,
SCM_F_WIND_EXPLICITLY);
- /* Reset vp->sp_max_since_gc so that the VM checks actually
+ /* Reset vp->sp_min_since_gc so that the VM checks actually
trigger. */
return_unused_stack_to_os (vp);
diff --git a/libguile/vm.h b/libguile/vm.h
index 8f88d0cd4..adac08593 100644
--- a/libguile/vm.h
+++ b/libguile/vm.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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
@@ -37,13 +37,14 @@ enum {
struct scm_vm {
scm_t_uint32 *ip; /* instruction pointer */
- SCM *sp; /* stack pointer */
- SCM *fp; /* frame pointer */
- SCM *stack_limit; /* stack limit address */
+ union scm_vm_stack_element *sp; /* stack pointer */
+ union scm_vm_stack_element *fp; /* frame pointer */
+ union scm_vm_stack_element *stack_limit; /* stack limit address */
int trace_level; /* traces enabled if trace_level > 0 */
- SCM *sp_max_since_gc; /* highest sp since last gc */
+ union scm_vm_stack_element *sp_min_since_gc; /* deepest sp since last gc */
size_t stack_size; /* stack size */
- SCM *stack_base; /* stack base address */
+ union scm_vm_stack_element *stack_bottom; /* lowest address in allocated stack */
+ union scm_vm_stack_element *stack_top; /* highest address in allocated stack */
SCM overflow_handler_stack; /* alist of max-stack-size -> thunk */
SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */
int engine; /* which vm engine we're using */
@@ -78,11 +79,13 @@ SCM_INTERNAL void scm_i_vm_free_stack (struct scm_vm *vp);
#define SCM_F_VM_CONT_REWINDABLE 0x2
struct scm_vm_cont {
- SCM *sp;
- SCM *fp;
+ /* FIXME: sp isn't needed, it's effectively the same as
+ stack_bottom */
+ union scm_vm_stack_element *sp;
+ union scm_vm_stack_element *fp;
scm_t_uint32 *ra;
scm_t_ptrdiff stack_size;
- SCM *stack_base;
+ union scm_vm_stack_element *stack_bottom;
scm_t_ptrdiff reloc;
scm_t_dynstack *dynstack;
scm_t_uint32 flags;
@@ -97,7 +100,9 @@ SCM_API SCM scm_load_compiled_with_vm (SCM file);
SCM_INTERNAL SCM scm_i_call_with_current_continuation (SCM proc);
SCM_INTERNAL SCM scm_i_capture_current_stack (void);
-SCM_INTERNAL SCM scm_i_vm_capture_stack (SCM *stack_base, SCM *fp, SCM *sp,
+SCM_INTERNAL SCM scm_i_vm_capture_stack (union scm_vm_stack_element *stack_top,
+ union scm_vm_stack_element *fp,
+ union scm_vm_stack_element *sp,
scm_t_uint32 *ra,
scm_t_dynstack *dynstack,
scm_t_uint32 flags);
From aa9f6b00827bd0c1a2f11274191f49b0ad31926d Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 18 Oct 2015 19:54:58 +0200
Subject: [PATCH 054/865] VM caches address of local 0 instead of FP
* libguile/vm-engine.c (vm_engine): Cache the address of local 0 instead
of the FP. This makes locals access a bit cheaper, but we still have
to negate the index. The right fix is to index relative to the SP
instead. That's a more involved change, so we punt until later.
---
libguile/vm-engine.c | 120 ++++++++++++++++++++++++-------------------
1 file changed, 66 insertions(+), 54 deletions(-)
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index f6cb0c49c..9415de5b1 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -107,7 +107,7 @@
{ \
SYNC_IP (); \
exp; \
- CACHE_FP (); \
+ CACHE_LOCALS (); \
} \
} while (0)
#else
@@ -128,37 +128,39 @@
RUN_HOOK0 (abort)
#define VM_HANDLE_INTERRUPTS \
- SCM_ASYNC_TICK_WITH_GUARD_CODE (thread, SYNC_IP (), CACHE_FP ())
+ SCM_ASYNC_TICK_WITH_GUARD_CODE (thread, SYNC_IP (), CACHE_LOCALS ())
/* Virtual Machine
The VM has three state bits: the instruction pointer (IP), the frame
- pointer (FP), and the stack pointer (SP). We cache the first two of
- these in machine registers, local to the VM, because they are used
- extensively by the VM. As the SP is used more by code outside the VM
- than by the VM itself, we don't bother caching it locally.
+ pointer (FP), and the stack pointer (SP). We cache the IP in a
+ machine register, local to the VM, because it is used extensively by
+ the VM. We cache the address of local 0 too, for now; when we change
+ to reference variables relative to the SP we'll cache the SP instead.
+ As it is, the SP is used more by code outside the VM than by the VM
+ itself, we don't bother caching it locally.
- Since the FP changes infrequently, relative to the IP, we keep vp->fp
- in sync with the local FP. This would be a big lose for the IP,
- though, so instead of updating vp->ip all the time, we call SYNC_IP
- whenever we would need to know the IP of the top frame. In practice,
- we need to SYNC_IP whenever we call out of the VM to a function that
- would like to walk the stack, perhaps as the result of an
- exception.
+ Keeping vp->ip in sync with the local IP would be a big lose, as it
+ is updated so often. Instead of updating vp->ip all the time, we
+ call SYNC_IP whenever we would need to know the IP of the top frame.
+ In practice, we need to SYNC_IP whenever we call out of the VM to a
+ function that would like to walk the stack, perhaps as the result of
+ an exception.
One more thing. We allow the stack to move, when it expands.
Therefore if you call out to a C procedure that could call Scheme
code, or otherwise push anything on the stack, you will need to
- CACHE_FP afterwards to restore the possibly-changed FP. */
+ CACHE_LOCALS afterwards to restore the possibly-changed address of
+ local 0. */
#define SYNC_IP() vp->ip = (ip)
-#define CACHE_FP() fp = (vp->fp)
+#define CACHE_LOCALS() locals = (vp->fp - 1)
#define CACHE_REGISTER() \
do { \
ip = vp->ip; \
- fp = vp->fp; \
+ CACHE_LOCALS (); \
} while (0)
@@ -179,7 +181,7 @@
{ \
SYNC_IP (); \
vm_expand_stack (vp, new_sp); \
- CACHE_FP (); \
+ CACHE_LOCALS (); \
} \
else \
vp->sp_min_since_gc = vp->sp = new_sp; \
@@ -246,9 +248,15 @@
case opcode:
#endif
-#define LOCAL_ADDRESS(i) SCM_FRAME_SLOT (fp, i)
-#define LOCAL_REF(i) SCM_FRAME_LOCAL (fp, i)
-#define LOCAL_SET(i,o) SCM_FRAME_LOCAL (fp, i) = o
+// This "locals + 1" is actually an optimization, because vp->fp points
+// on before the zeroeth local. The result is to reference locals[-i].
+// In the future we should change to reference locals relative to the SP
+// and cache the SP instead, which would give direct (non-negated)
+// indexing off the SP, which is more in line with addressing modes
+// supported by common CPUs.
+#define LOCAL_ADDRESS(i) SCM_FRAME_SLOT (locals + 1, i)
+#define LOCAL_REF(i) SCM_FRAME_LOCAL (locals + 1, i)
+#define LOCAL_SET(i,o) SCM_FRAME_LOCAL (locals + 1, i) = o
#define VARIABLE_REF(v) SCM_VARIABLE_REF (v)
#define VARIABLE_SET(v,o) SCM_VARIABLE_SET (v, o)
@@ -260,9 +268,10 @@
union scm_vm_stack_element *old_fp; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (2); \
- old_fp = fp; \
- ip = SCM_FRAME_RETURN_ADDRESS (fp); \
- fp = vp->fp = SCM_FRAME_DYNAMIC_LINK (fp); \
+ old_fp = vp->fp; \
+ ip = SCM_FRAME_RETURN_ADDRESS (vp->fp); \
+ vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp); \
+ CACHE_LOCALS (); \
/* Clear frame. */ \
old_fp[0].scm = SCM_BOOL_F; \
old_fp[1].scm = SCM_BOOL_F; \
@@ -280,9 +289,9 @@
SCM vals = vals_; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (3); \
- SCM_FRAME_LOCAL (fp, 0) = vm_builtin_apply; \
- SCM_FRAME_LOCAL (fp, 1) = vm_builtin_values; \
- SCM_FRAME_LOCAL (fp, 2) = vals; \
+ SCM_FRAME_LOCAL (vp->fp, 0) = vm_builtin_apply; \
+ SCM_FRAME_LOCAL (vp->fp, 1) = vm_builtin_values; \
+ SCM_FRAME_LOCAL (vp->fp, 2) = vals; \
ip = (scm_t_uint32 *) vm_builtin_apply_code; \
goto op_tail_apply; \
} while (0)
@@ -355,7 +364,7 @@
SCM res; \
SYNC_IP (); \
res = srel (x, y); \
- CACHE_FP (); \
+ CACHE_LOCALS (); \
if ((ip[1] & 0x1) ? scm_is_false (res) : scm_is_true (res)) \
{ \
scm_t_int32 offset = ip[1]; \
@@ -382,7 +391,7 @@
#define RETURN(x) \
do { LOCAL_SET (dst, x); NEXT (1); } while (0)
#define RETURN_EXP(exp) \
- do { SCM __x; SYNC_IP (); __x = exp; CACHE_FP (); RETURN (__x); } while (0)
+ do { SCM __x; SYNC_IP (); __x = exp; CACHE_LOCALS (); RETURN (__x); } while (0)
/* The maximum/minimum tagged integers. */
#define INUM_MAX \
@@ -429,7 +438,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
/* Frame pointer: A pointer into the stack, off of which we index
arguments and local variables. Pushed at function calls, popped on
returns. */
- register union scm_vm_stack_element *fp FP_REG;
+ register union scm_vm_stack_element *locals FP_REG;
/* Current opcode: A cache of *ip. */
register scm_t_uint32 op;
@@ -524,9 +533,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ret = scm_values (ret);
}
- vp->ip = SCM_FRAME_RETURN_ADDRESS (fp);
- vp->sp = SCM_FRAME_PREVIOUS_SP (fp);
- vp->fp = SCM_FRAME_DYNAMIC_LINK (fp);
+ vp->ip = SCM_FRAME_RETURN_ADDRESS (vp->fp);
+ vp->sp = SCM_FRAME_PREVIOUS_SP (vp->fp);
+ vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp);
return ret;
}
@@ -556,10 +565,11 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
PUSH_CONTINUATION_HOOK ();
- old_fp = fp;
- fp = vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
- SCM_FRAME_SET_DYNAMIC_LINK (fp, old_fp);
- SCM_FRAME_SET_RETURN_ADDRESS (fp, ip + 2);
+ old_fp = vp->fp;
+ vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
+ CACHE_LOCALS ();
+ SCM_FRAME_SET_DYNAMIC_LINK (vp->fp, old_fp);
+ SCM_FRAME_SET_RETURN_ADDRESS (vp->fp, ip + 2);
RESET_FRAME (nlocals);
@@ -597,10 +607,11 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
PUSH_CONTINUATION_HOOK ();
- old_fp = fp;
- fp = vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
- SCM_FRAME_SET_DYNAMIC_LINK (fp, old_fp);
- SCM_FRAME_SET_RETURN_ADDRESS (fp, ip + 3);
+ old_fp = vp->fp;
+ vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
+ CACHE_LOCALS ();
+ SCM_FRAME_SET_DYNAMIC_LINK (vp->fp, old_fp);
+ SCM_FRAME_SET_RETURN_ADDRESS (vp->fp, ip + 3);
RESET_FRAME (nlocals);
@@ -759,9 +770,10 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
VM_HANDLE_INTERRUPTS;
- old_fp = fp;
- ip = SCM_FRAME_RETURN_ADDRESS (fp);
- fp = vp->fp = SCM_FRAME_DYNAMIC_LINK (fp);
+ old_fp = vp->fp;
+ ip = SCM_FRAME_RETURN_ADDRESS (vp->fp);
+ vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp);
+ CACHE_LOCALS ();
/* Clear stack frame. */
old_fp[0].scm = SCM_BOOL_F;
@@ -850,7 +862,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
abort ();
}
- CACHE_FP ();
+ CACHE_LOCALS ();
if (SCM_UNLIKELY (SCM_VALUESP (ret)))
/* multiple values returned to continuation */
@@ -884,7 +896,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ret = scm_i_foreign_call (scm_inline_cons (thread, cif, pointer),
vp->sp);
- CACHE_FP ();
+ CACHE_LOCALS ();
if (SCM_UNLIKELY (SCM_VALUESP (ret)))
/* multiple values returned to continuation */
@@ -1011,9 +1023,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SYNC_IP ();
dynstack = scm_dynstack_capture_all (&thread->dynstack);
vm_cont = scm_i_vm_capture_stack (vp->stack_top,
- SCM_FRAME_DYNAMIC_LINK (fp),
- SCM_FRAME_PREVIOUS_SP (fp),
- SCM_FRAME_RETURN_ADDRESS (fp),
+ SCM_FRAME_DYNAMIC_LINK (vp->fp),
+ SCM_FRAME_PREVIOUS_SP (vp->fp),
+ SCM_FRAME_RETURN_ADDRESS (vp->fp),
dynstack,
0);
/* FIXME: Seems silly to capture the registers here, when they are
@@ -1477,7 +1489,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is equal? to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- // FIXME: Should sync_ip before calling out and cache_fp before coming
+ // FIXME: Should sync_ip before calling out and cache_locals before coming
// back! Another reason to remove this opcode!
VM_DEFINE_OP (40, br_if_equal, "br-if-equal", OP2 (U8_U12_U12, B1_X7_L24))
{
@@ -1893,7 +1905,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SYNC_IP ();
var = scm_lookup (LOCAL_REF (sym));
- CACHE_FP ();
+ CACHE_LOCALS ();
if (ip[1] & 0x1)
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (LOCAL_REF (sym)));
LOCAL_SET (dst, var);
@@ -1912,7 +1924,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, sym, val);
SYNC_IP ();
scm_define (LOCAL_REF (sym), LOCAL_REF (val));
- CACHE_FP ();
+ CACHE_LOCALS ();
NEXT (1);
}
@@ -1972,7 +1984,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
mod = scm_the_root_module ();
var = scm_module_lookup (mod, sym);
- CACHE_FP ();
+ CACHE_LOCALS ();
if (ip[4] & 0x1)
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (sym));
@@ -2033,7 +2045,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
else
var = scm_private_lookup (SCM_CDR (modname), sym);
- CACHE_FP ();
+ CACHE_LOCALS ();
if (ip[4] & 0x1)
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (sym));
@@ -2075,7 +2087,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
flags = escape_only_p ? SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY : 0;
scm_dynstack_push_prompt (&thread->dynstack, flags,
LOCAL_REF (tag),
- vp->stack_top - fp,
+ vp->stack_top - vp->fp,
vp->stack_top - LOCAL_ADDRESS (proc_slot),
ip + offset,
registers);
From 30c06bfbb3db6e8c05f2ee6e7866d4ecf5838482 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 18 Oct 2015 20:12:15 +0200
Subject: [PATCH 055/865] Remove sp from scm_vm_cont
* libguile/vm.h (struct scm_vm_cont): Remove "sp" member; it's always
the same as stack_bottom.
* libguile/vm.c (scm_i_vm_cont_to_frame, scm_i_vm_capture_stack):
(vm_return_to_continuation_inner):
* libguile/stacks.c (scm_make_stack):
* libguile/continuations.c (scm_i_continuation_to_frame): Adapt.
---
libguile/continuations.c | 2 +-
libguile/stacks.c | 2 +-
libguile/vm.c | 5 ++---
libguile/vm.h | 3 ---
4 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/libguile/continuations.c b/libguile/continuations.c
index 7cc3cea10..c0a2bd8ae 100644
--- a/libguile/continuations.c
+++ b/libguile/continuations.c
@@ -186,7 +186,7 @@ scm_i_continuation_to_frame (SCM continuation, struct scm_frame *frame)
stack_top = data->stack_bottom + data->stack_size;
frame->stack_holder = data;
frame->fp_offset = stack_top - (data->fp + data->reloc);
- frame->sp_offset = stack_top - (data->sp + data->reloc);
+ frame->sp_offset = data->stack_size;
frame->ip = data->ra;
return 1;
diff --git a/libguile/stacks.c b/libguile/stacks.c
index ec3ec789f..366176b10 100644
--- a/libguile/stacks.c
+++ b/libguile/stacks.c
@@ -330,7 +330,7 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
kind = SCM_VM_FRAME_KIND_CONT;
frame.stack_holder = c;
frame.fp_offset = stack_top - (c->fp + c->reloc);
- frame.sp_offset = stack_top - (c->sp + c->reloc);
+ frame.sp_offset = c->stack_size;
frame.ip = c->ra;
}
else if (SCM_VM_FRAME_P (obj))
diff --git a/libguile/vm.c b/libguile/vm.c
index d5a72727f..74dce6351 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -120,7 +120,7 @@ scm_i_vm_cont_to_frame (SCM cont, struct scm_frame *frame)
stack_top = data->stack_bottom + data->stack_size;
frame->stack_holder = data;
frame->fp_offset = stack_top - (data->fp + data->reloc);
- frame->sp_offset = stack_top - (data->sp + data->reloc);
+ frame->sp_offset = data->stack_size;
frame->ip = data->ra;
return 1;
@@ -142,7 +142,6 @@ scm_i_vm_capture_stack (union scm_vm_stack_element *stack_top,
p->stack_bottom = scm_gc_malloc (p->stack_size * sizeof (*p->stack_bottom),
"capture_vm_cont");
p->ra = ra;
- p->sp = sp;
p->fp = fp;
memcpy (p->stack_bottom, sp, p->stack_size * sizeof (*p->stack_bottom));
p->reloc = (p->stack_bottom + p->stack_size) - stack_top;
@@ -178,7 +177,7 @@ vm_return_to_continuation_inner (void *data_ptr)
memcpy (vp->stack_top - cp->stack_size,
cp->stack_bottom,
cp->stack_size * sizeof (*cp->stack_bottom));
- vm_restore_sp (vp, cp->sp + reloc);
+ vm_restore_sp (vp, vp->stack_top - cp->stack_size);
if (reloc)
{
diff --git a/libguile/vm.h b/libguile/vm.h
index adac08593..936633d21 100644
--- a/libguile/vm.h
+++ b/libguile/vm.h
@@ -79,9 +79,6 @@ SCM_INTERNAL void scm_i_vm_free_stack (struct scm_vm *vp);
#define SCM_F_VM_CONT_REWINDABLE 0x2
struct scm_vm_cont {
- /* FIXME: sp isn't needed, it's effectively the same as
- stack_bottom */
- union scm_vm_stack_element *sp;
union scm_vm_stack_element *fp;
scm_t_uint32 *ra;
scm_t_ptrdiff stack_size;
From 8f027385db228d68193ad7316cf0b79489ac038b Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 18 Oct 2015 20:25:43 +0200
Subject: [PATCH 056/865] Rename union scm_vm_stack_element members
* libguile/frames.h (union scm_vm_stack_element): Rename members from
scm, ip, etc to as_scm, as_ip, etc. Adapt users.
---
libguile/control.c | 6 +++---
libguile/foreign.c | 2 +-
libguile/frames.h | 26 +++++++++++++-------------
libguile/vm-engine.c | 22 +++++++++++-----------
libguile/vm.c | 21 +++++++++++----------
5 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/libguile/control.c b/libguile/control.c
index a3457342b..c0bc62ddb 100644
--- a/libguile/control.c
+++ b/libguile/control.c
@@ -52,7 +52,7 @@ scm_i_prompt_pop_abort_args_x (struct scm_vm *vp,
n = stack_depth - saved_stack_depth;
for (i = 0; i < n; i++)
- vals = scm_cons (vp->sp[i].scm, vals);
+ vals = scm_cons (vp->sp[i].as_scm, vals);
vp->sp += n;
@@ -169,9 +169,9 @@ scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM *argv,
abort ();
/* Push vals */
- vp->sp[n].scm = cont;
+ vp->sp[n].as_scm = cont;
for (i = 0; i < n; i++)
- vp->sp[n - i - 1].scm = argv[i];
+ vp->sp[n - i - 1].as_scm = argv[i];
/* Jump! */
SCM_I_LONGJMP (*registers, 1);
diff --git a/libguile/foreign.c b/libguile/foreign.c
index 3ac06591d..864019e63 100644
--- a/libguile/foreign.c
+++ b/libguile/foreign.c
@@ -1016,7 +1016,7 @@ scm_i_foreign_call (SCM foreign, const union scm_vm_stack_element *argv)
args[i] = (void *) ROUND_UP ((scm_t_uintptr) data + off,
cif->arg_types[i]->alignment);
assert ((scm_t_uintptr) args[i] % cif->arg_types[i]->alignment == 0);
- unpack (cif->arg_types[i], args[i], argv[cif->nargs - i - 1].scm, 0);
+ unpack (cif->arg_types[i], args[i], argv[cif->nargs - i - 1].as_scm, 0);
}
/* Prepare space for the return value. On some platforms, such as
diff --git a/libguile/frames.h b/libguile/frames.h
index c2f1e57db..870477d53 100644
--- a/libguile/frames.h
+++ b/libguile/frames.h
@@ -88,23 +88,23 @@
/* Each element on the stack occupies the same amount of space. */
union scm_vm_stack_element
{
- union scm_vm_stack_element *fp;
- scm_t_uint32 *ip;
- SCM scm;
+ union scm_vm_stack_element *as_fp;
+ scm_t_uint32 *as_ip;
+ SCM as_scm;
/* For GC purposes. */
- void *ptr;
- scm_t_bits bits;
+ void *as_ptr;
+ scm_t_bits as_bits;
};
-#define SCM_FRAME_PREVIOUS_SP(fp_) ((fp_) + 2)
-#define SCM_FRAME_RETURN_ADDRESS(fp_) ((fp_)[0].ip)
-#define SCM_FRAME_SET_RETURN_ADDRESS(fp_, ra) ((fp_)[0].ip = (ra))
-#define SCM_FRAME_DYNAMIC_LINK(fp_) ((fp_)[1].fp)
-#define SCM_FRAME_SET_DYNAMIC_LINK(fp_, dl) ((fp_)[1].fp = (dl))
-#define SCM_FRAME_SLOT(fp_,i) ((fp_) - (i) - 1)
-#define SCM_FRAME_LOCAL(fp_,i) (SCM_FRAME_SLOT (fp_, i)->scm)
-#define SCM_FRAME_NUM_LOCALS(fp_, sp) ((fp_) - (sp))
+#define SCM_FRAME_PREVIOUS_SP(fp) ((fp) + 2)
+#define SCM_FRAME_RETURN_ADDRESS(fp) ((fp)[0].as_ip)
+#define SCM_FRAME_SET_RETURN_ADDRESS(fp, ra) ((fp)[0].as_ip = (ra))
+#define SCM_FRAME_DYNAMIC_LINK(fp) ((fp)[1].as_fp)
+#define SCM_FRAME_SET_DYNAMIC_LINK(fp, dl) ((fp)[1].as_fp = (dl))
+#define SCM_FRAME_SLOT(fp,i) ((fp) - (i) - 1)
+#define SCM_FRAME_LOCAL(fp,i) (SCM_FRAME_SLOT (fp, i)->as_scm)
+#define SCM_FRAME_NUM_LOCALS(fp, sp) ((fp) - (sp))
/*
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 9415de5b1..daea0bfe2 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -268,13 +268,13 @@
union scm_vm_stack_element *old_fp; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (2); \
- old_fp = vp->fp; \
- ip = SCM_FRAME_RETURN_ADDRESS (vp->fp); \
- vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp); \
- CACHE_LOCALS (); \
+ old_fp = vp->fp; \
+ ip = SCM_FRAME_RETURN_ADDRESS (vp->fp); \
+ vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp); \
+ CACHE_LOCALS (); \
/* Clear frame. */ \
- old_fp[0].scm = SCM_BOOL_F; \
- old_fp[1].scm = SCM_BOOL_F; \
+ old_fp[0].as_scm = SCM_BOOL_F; \
+ old_fp[1].as_scm = SCM_BOOL_F; \
/* Leave proc. */ \
SCM_FRAME_LOCAL (old_fp, 1) = val; \
vp->sp = SCM_FRAME_SLOT (old_fp, 1); \
@@ -289,9 +289,9 @@
SCM vals = vals_; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (3); \
- SCM_FRAME_LOCAL (vp->fp, 0) = vm_builtin_apply; \
- SCM_FRAME_LOCAL (vp->fp, 1) = vm_builtin_values; \
- SCM_FRAME_LOCAL (vp->fp, 2) = vals; \
+ SCM_FRAME_LOCAL (vp->fp, 0) = vm_builtin_apply; \
+ SCM_FRAME_LOCAL (vp->fp, 1) = vm_builtin_values; \
+ SCM_FRAME_LOCAL (vp->fp, 2) = vals; \
ip = (scm_t_uint32 *) vm_builtin_apply_code; \
goto op_tail_apply; \
} while (0)
@@ -776,8 +776,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
CACHE_LOCALS ();
/* Clear stack frame. */
- old_fp[0].scm = SCM_BOOL_F;
- old_fp[1].scm = SCM_BOOL_F;
+ old_fp[0].as_scm = SCM_BOOL_F;
+ old_fp[1].as_scm = SCM_BOOL_F;
POP_CONTINUATION_HOOK (old_fp);
diff --git a/libguile/vm.c b/libguile/vm.c
index 74dce6351..bc9f29080 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -218,9 +218,9 @@ vm_return_to_continuation (struct scm_vm *vp, SCM cont, size_t n,
copy on an empty frame and the return values, as the continuation
expects. */
vm_push_sp (vp, vp->sp - 3 - n);
- vp->sp[n+2].scm = SCM_BOOL_F;
- vp->sp[n+1].scm = SCM_BOOL_F;
- vp->sp[n].scm = SCM_BOOL_F;
+ vp->sp[n+2].as_scm = SCM_BOOL_F;
+ vp->sp[n+1].as_scm = SCM_BOOL_F;
+ vp->sp[n].as_scm = SCM_BOOL_F;
memcpy(vp->sp, argv_copy, n * sizeof (union scm_vm_stack_element));
vp->ip = cp->ra;
@@ -298,7 +298,7 @@ vm_dispatch_hook (struct scm_vm *vp, int hook_num,
SCM args[2];
args[0] = SCM_PACK_POINTER (frame);
- args[1] = argv[0].scm;
+ args[1] = argv[0].as_scm;
scm_c_run_hookn (hook, args, 2);
}
else
@@ -307,7 +307,7 @@ vm_dispatch_hook (struct scm_vm *vp, int hook_num,
int i;
for (i = 0; i < n; i++)
- args = scm_cons (argv[i].scm, args);
+ args = scm_cons (argv[i].as_scm, args);
scm_c_run_hook (hook, scm_cons (SCM_PACK_POINTER (frame), args));
}
@@ -352,7 +352,7 @@ vm_abort (struct scm_vm *vp, SCM tag, size_t nargs,
argv = alloca (nargs * sizeof (SCM));
for (i = 0; i < nargs; i++)
- argv[i] = vp->sp[nargs - i - 1].scm;
+ argv[i] = vp->sp[nargs - i - 1].as_scm;
vp->sp = vp->fp;
@@ -422,7 +422,7 @@ vm_reinstate_partial_continuation (struct scm_vm *vp, SCM cont, size_t nargs,
/* The resume continuation will expect ARGS on the stack as if from a
multiple-value return. Fill in the closure slot with #f, and copy
the arguments into place. */
- vp->sp[nargs].scm = SCM_BOOL_F;
+ vp->sp[nargs].as_scm = SCM_BOOL_F;
memcpy (vp->sp, args, nargs * sizeof (*args));
/* The prompt captured a slice of the dynamic stack. Here we wind
@@ -973,7 +973,8 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
size_t slot = nlocals - 1;
for (slot = nlocals - 1; sp < fp; sp++, slot--)
{
- if (SCM_NIMP (sp->scm) && sp->ptr >= lower && sp->ptr <= upper)
+ if (SCM_NIMP (sp->as_scm) &&
+ sp->as_ptr >= lower && sp->as_ptr <= upper)
{
if (dead_slots)
{
@@ -981,12 +982,12 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
{
/* This value may become dead as a result of GC,
so we can't just leave it on the stack. */
- sp->scm = SCM_UNSPECIFIED;
+ sp->as_scm = SCM_UNSPECIFIED;
continue;
}
}
- mark_stack_ptr = GC_mark_and_push (sp->ptr,
+ mark_stack_ptr = GC_mark_and_push (sp->as_ptr,
mark_stack_ptr,
mark_stack_limit,
NULL);
From 72353de77d0a06f158d8af66a2540015658e2574 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 18 Oct 2015 22:59:23 +0200
Subject: [PATCH 057/865] Replace dynamic link on stack with previous frame
size
* libguile/frames.h (SCM_FRAME_DYNAMIC_LINK)
(SCM_FRAME_SET_DYNAMIC_LINK): Instead of storing the absolute value of
the previous FP, store its offset from the current FP. This allows us
to avoid relinking when composing continuations or when relocating the
stack.
* libguile/frames.c (scm_frame_dynamic_link, scm_c_frame_previous): No
need to relocate the dynamic link.
* libguile/vm.c (vm_return_to_continuation_inner):
(vm_reinstate_partial_continuation_inner, vm_expand_stack_inner):
Don't relocate the frame pointer chain.
(scm_i_vm_mark_stack): Terminate when FP is above stack_top, not when
0.
(make_vm): Init FP to stack_top.
---
libguile/frames.c | 12 ++----------
libguile/frames.h | 6 +++---
libguile/vm.c | 49 +++++++----------------------------------------
3 files changed, 12 insertions(+), 55 deletions(-)
diff --git a/libguile/frames.c b/libguile/frames.c
index f89b0fd5b..a1c7f3e71 100644
--- a/libguile/frames.c
+++ b/libguile/frames.c
@@ -309,8 +309,6 @@ SCM_DEFINE (scm_frame_return_address, "frame-return-address", 1, 0, 0,
}
#undef FUNC_NAME
-#define RELOC(kind, frame, val) ((val) + frame_offset (kind, frame))
-
SCM_DEFINE (scm_frame_dynamic_link, "frame-dynamic-link", 1, 0, 0,
(SCM frame),
"")
@@ -320,8 +318,7 @@ SCM_DEFINE (scm_frame_dynamic_link, "frame-dynamic-link", 1, 0, 0,
/* fixme: munge fp if holder is a continuation */
return scm_from_uintptr_t
((scm_t_uintptr)
- RELOC (SCM_VM_FRAME_KIND (frame), SCM_VM_FRAME_DATA (frame),
- SCM_FRAME_DYNAMIC_LINK (SCM_VM_FRAME_FP (frame))));
+ SCM_FRAME_DYNAMIC_LINK (SCM_VM_FRAME_FP (frame)));
}
#undef FUNC_NAME
@@ -339,12 +336,7 @@ scm_c_frame_previous (enum scm_vm_frame_kind kind, struct scm_frame *frame)
new_fp = SCM_FRAME_DYNAMIC_LINK (this_fp);
- if (!new_fp)
- return 0;
-
- new_fp = RELOC (kind, frame, new_fp);
-
- if (new_fp > stack_top)
+ if (new_fp >= stack_top)
return 0;
new_sp = SCM_FRAME_PREVIOUS_SP (this_fp);
diff --git a/libguile/frames.h b/libguile/frames.h
index 870477d53..e1130e94b 100644
--- a/libguile/frames.h
+++ b/libguile/frames.h
@@ -88,7 +88,7 @@
/* Each element on the stack occupies the same amount of space. */
union scm_vm_stack_element
{
- union scm_vm_stack_element *as_fp;
+ scm_t_uintptr as_uint;
scm_t_uint32 *as_ip;
SCM as_scm;
@@ -100,8 +100,8 @@ union scm_vm_stack_element
#define SCM_FRAME_PREVIOUS_SP(fp) ((fp) + 2)
#define SCM_FRAME_RETURN_ADDRESS(fp) ((fp)[0].as_ip)
#define SCM_FRAME_SET_RETURN_ADDRESS(fp, ra) ((fp)[0].as_ip = (ra))
-#define SCM_FRAME_DYNAMIC_LINK(fp) ((fp)[1].as_fp)
-#define SCM_FRAME_SET_DYNAMIC_LINK(fp, dl) ((fp)[1].as_fp = (dl))
+#define SCM_FRAME_DYNAMIC_LINK(fp) ((fp) + (fp)[1].as_uint)
+#define SCM_FRAME_SET_DYNAMIC_LINK(fp, dl) ((fp)[1].as_uint = ((dl) - (fp)))
#define SCM_FRAME_SLOT(fp,i) ((fp) - (i) - 1)
#define SCM_FRAME_LOCAL(fp,i) (SCM_FRAME_SLOT (fp, i)->as_scm)
#define SCM_FRAME_NUM_LOCALS(fp, sp) ((fp) - (sp))
diff --git a/libguile/vm.c b/libguile/vm.c
index bc9f29080..2db079550 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -179,21 +179,6 @@ vm_return_to_continuation_inner (void *data_ptr)
cp->stack_size * sizeof (*cp->stack_bottom));
vm_restore_sp (vp, vp->stack_top - cp->stack_size);
- if (reloc)
- {
- union scm_vm_stack_element *fp = vp->fp;
- while (fp)
- {
- union scm_vm_stack_element *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
- if (next_fp)
- {
- next_fp += reloc;
- SCM_FRAME_SET_DYNAMIC_LINK (fp, next_fp);
- }
- fp = next_fp;
- }
- }
-
return NULL;
}
@@ -385,13 +370,6 @@ vm_reinstate_partial_continuation_inner (void *data_ptr)
vp->fp = cp->fp + reloc;
vp->ip = cp->ra;
- /* now relocate frame pointers */
- {
- union scm_vm_stack_element *fp;
- for (fp = vp->fp; fp < base_fp; fp = SCM_FRAME_DYNAMIC_LINK (fp))
- SCM_FRAME_SET_DYNAMIC_LINK (fp, SCM_FRAME_DYNAMIC_LINK (fp) + reloc);
- }
-
data->reloc = reloc;
return NULL;
@@ -876,7 +854,7 @@ make_vm (void)
vp->ip = NULL;
vp->sp = vp->stack_top;
vp->sp_min_since_gc = vp->sp;
- vp->fp = NULL;
+ vp->fp = vp->stack_top;
vp->engine = vm_default_engine;
vp->trace_level = 0;
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
@@ -967,7 +945,9 @@ scm_i_vm_mark_stack (struct scm_vm *vp, struct GC_ms_entry *mark_stack_ptr,
memset (&cache, 0, sizeof (cache));
- for (fp = vp->fp, sp = vp->sp; fp; fp = SCM_FRAME_DYNAMIC_LINK (fp))
+ for (fp = vp->fp, sp = vp->sp;
+ fp < vp->stack_top;
+ fp = SCM_FRAME_DYNAMIC_LINK (fp))
{
scm_t_ptrdiff nlocals = SCM_FRAME_NUM_LOCALS (fp, sp);
size_t slot = nlocals - 1;
@@ -1047,24 +1027,9 @@ vm_expand_stack_inner (void *data_ptr)
vp->stack_limit = vp->stack_bottom;
reloc = vp->stack_top - old_top;
- if (reloc)
- {
- union scm_vm_stack_element *fp;
- if (vp->fp)
- vp->fp += reloc;
- data->new_sp += reloc;
- fp = vp->fp;
- while (fp)
- {
- union scm_vm_stack_element *next_fp = SCM_FRAME_DYNAMIC_LINK (fp);
- if (next_fp)
- {
- next_fp += reloc;
- SCM_FRAME_SET_DYNAMIC_LINK (fp, next_fp);
- }
- fp = next_fp;
- }
- }
+ if (vp->fp)
+ vp->fp += reloc;
+ data->new_sp += reloc;
return new_bottom;
}
From 0da0308b8479aab335230675c94e0773985f06d6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Tue, 20 Oct 2015 20:06:40 +0200
Subject: [PATCH 058/865] Prepare for SP-addressed locals
* libguile/vm-engine.c: Renumber opcodes, and take the opportunity to
fold recent additions into more logical places. Be more precise when
describing the encoding of operands, to shuffle local references only
and not constants, immediates, or other such values.
(SP_REF, SP_SET): New helpers.
(BR_BINARY, BR_ARITHMETIC): Take full 24-bit operands. Our shuffle
strategy is to emit push when needed to bring far locals near, then
pop afterwards, shuffling away far destination values as needed; but
that doesn't work for conditionals, unless we introduce a trampoline.
Let's just do the simple thing for now. Native compilation will use
condition codes.
(push, pop, drop): Back from the dead! We'll only use these for
temporary shuffling though, when an opcode can't address the full
24-bit range.
(long-fmov): New instruction, like long-mov but relative to the frame
pointer.
(load-typed-array, make-array): Don't use a compressed encoding so
that we can avoid the shuffling case. It would be a pain, given that
they have so many operands already.
* module/language/bytecode.scm (compute-instruction-arity): Update for
new instrution word encodings.
* module/system/vm/assembler.scm: Update to expose some opcodes
directly, without the need for shuffling wrappers. Adapt to
instruction word encodings change.
* module/system/vm/disassembler.scm (disassembler): Adapt to instruction
coding change.
---
libguile/instructions.c | 52 +--
libguile/vm-engine.c | 581 +++++++++++++++++-------------
module/language/bytecode.scm | 42 ++-
module/system/vm/assembler.scm | 135 ++++---
module/system/vm/disassembler.scm | 70 ++--
5 files changed, 494 insertions(+), 386 deletions(-)
diff --git a/libguile/instructions.c b/libguile/instructions.c
index e474cf5d5..003fd5425 100644
--- a/libguile/instructions.c
+++ b/libguile/instructions.c
@@ -31,30 +31,34 @@ SCM_SYMBOL (sym_left_arrow, "<-");
SCM_SYMBOL (sym_bang, "!");
-#define OP_HAS_ARITY (1U << 0)
-
#define FOR_EACH_INSTRUCTION_WORD_TYPE(M) \
M(X32) \
- M(U8_X24) \
- M(U8_U24) \
- M(U8_L24) \
- M(U8_U8_I16) \
- M(U8_U8_U8_U8) \
- M(U8_U12_U12) \
- M(U32) /* Unsigned. */ \
+ M(X8_S24) \
+ M(X8_F24) \
+ M(X8_L24) \
+ M(X8_C24) \
+ M(X8_S8_I16) \
+ M(X8_S12_S12) \
+ M(X8_S12_C12) \
+ M(X8_C12_C12) \
+ M(X8_F12_F12) \
+ M(X8_S8_S8_S8) \
+ M(X8_S8_C8_S8) \
+ M(X8_S8_S8_C8) \
+ M(C8_C24) \
+ M(C32) /* Unsigned. */ \
M(I32) /* Immediate. */ \
M(A32) /* Immediate, high bits. */ \
M(B32) /* Immediate, low bits. */ \
M(N32) /* Non-immediate. */ \
- M(S32) /* Scheme value (indirected). */ \
+ M(R32) /* Scheme value (indirected). */ \
M(L32) /* Label. */ \
M(LO32) /* Label with offset. */ \
- M(X8_U24) \
- M(X8_U12_U12) \
- M(X8_L24) \
+ M(B1_C7_L24) \
M(B1_X7_L24) \
- M(B1_U7_L24) \
- M(B1_X7_U24) \
+ M(B1_X7_C24) \
+ M(B1_X7_S24) \
+ M(B1_X7_F24) \
M(B1_X31)
#define TYPE_WIDTH 5
@@ -73,7 +77,7 @@ static SCM word_type_symbols[] =
#undef FALSE
};
-#define OP(n,type) ((type) << (n*TYPE_WIDTH))
+#define OP(n,type) (((type) + 1) << (n*TYPE_WIDTH))
/* The VM_DEFINE_OP macro uses a CPP-based DSL to describe what kinds of
arguments each instruction takes. This piece of code is the only
@@ -99,8 +103,12 @@ static SCM word_type_symbols[] =
#define OP_DST (1 << (TYPE_WIDTH * 5))
-#define WORD_TYPE(n, word) \
+#define WORD_TYPE_AND_FLAG(n, word) \
(((word) >> ((n) * TYPE_WIDTH)) & ((1 << TYPE_WIDTH) - 1))
+#define WORD_TYPE(n, word) \
+ (WORD_TYPE_AND_FLAG (n, word) - 1)
+#define HAS_WORD(n, word) \
+ (WORD_TYPE_AND_FLAG (n, word) != 0)
/* Scheme interface */
@@ -112,15 +120,15 @@ parse_instruction (scm_t_uint8 opcode, const char *name, scm_t_uint32 meta)
/* Format: (name opcode word0 word1 ...) */
- if (WORD_TYPE (4, meta))
+ if (HAS_WORD (4, meta))
len = 5;
- else if (WORD_TYPE (3, meta))
+ else if (HAS_WORD (3, meta))
len = 4;
- else if (WORD_TYPE (2, meta))
+ else if (HAS_WORD (2, meta))
len = 3;
- else if (WORD_TYPE (1, meta))
+ else if (HAS_WORD (1, meta))
len = 2;
- else if (WORD_TYPE (0, meta))
+ else if (HAS_WORD (0, meta))
len = 1;
else
abort ();
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index daea0bfe2..df7a528eb 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -258,6 +258,9 @@
#define LOCAL_REF(i) SCM_FRAME_LOCAL (locals + 1, i)
#define LOCAL_SET(i,o) SCM_FRAME_LOCAL (locals + 1, i) = o
+#define SP_REF(i) (vp->sp[i].as_scm)
+#define SP_SET(i,o) (vp->sp[i].as_scm = o)
+
#define VARIABLE_REF(v) SCM_VARIABLE_REF (v)
#define VARIABLE_SET(v,o) SCM_VARIABLE_SET (v, o)
#define VARIABLE_BOUNDP(v) (!scm_is_eq (VARIABLE_REF (v), SCM_UNDEFINED))
@@ -323,57 +326,59 @@
NEXT (2)
#define BR_BINARY(x, y, exp) \
- scm_t_uint16 a, b; \
+ scm_t_uint32 a, b; \
SCM x, y; \
- UNPACK_12_12 (op, a, b); \
+ UNPACK_24 (op, a); \
+ UNPACK_24 (ip[1], b); \
x = LOCAL_REF (a); \
y = LOCAL_REF (b); \
- if ((ip[1] & 0x1) ? !(exp) : (exp)) \
+ if ((ip[2] & 0x1) ? !(exp) : (exp)) \
{ \
- scm_t_int32 offset = ip[1]; \
+ scm_t_int32 offset = ip[2]; \
offset >>= 8; /* Sign-extending shift. */ \
if (offset <= 0) \
VM_HANDLE_INTERRUPTS; \
NEXT (offset); \
} \
- NEXT (2)
+ NEXT (3)
#define BR_ARITHMETIC(crel,srel) \
{ \
- scm_t_uint16 a, b; \
+ scm_t_uint32 a, b; \
SCM x, y; \
- UNPACK_12_12 (op, a, b); \
+ UNPACK_24 (op, a); \
+ UNPACK_24 (ip[1], b); \
x = LOCAL_REF (a); \
y = LOCAL_REF (b); \
if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) \
{ \
scm_t_signed_bits x_bits = SCM_UNPACK (x); \
scm_t_signed_bits y_bits = SCM_UNPACK (y); \
- if ((ip[1] & 0x1) ? !(x_bits crel y_bits) : (x_bits crel y_bits)) \
+ if ((ip[2] & 0x1) ? !(x_bits crel y_bits) : (x_bits crel y_bits)) \
{ \
- scm_t_int32 offset = ip[1]; \
+ scm_t_int32 offset = ip[2]; \
offset >>= 8; /* Sign-extending shift. */ \
if (offset <= 0) \
VM_HANDLE_INTERRUPTS; \
NEXT (offset); \
} \
- NEXT (2); \
+ NEXT (3); \
} \
else \
{ \
SCM res; \
SYNC_IP (); \
res = srel (x, y); \
- CACHE_LOCALS (); \
- if ((ip[1] & 0x1) ? scm_is_false (res) : scm_is_true (res)) \
+ CACHE_LOCALS (); \
+ if ((ip[2] & 0x1) ? scm_is_false (res) : scm_is_true (res)) \
{ \
- scm_t_int32 offset = ip[1]; \
+ scm_t_int32 offset = ip[2]; \
offset >>= 8; /* Sign-extending shift. */ \
if (offset <= 0) \
VM_HANDLE_INTERRUPTS; \
NEXT (offset); \
} \
- NEXT (2); \
+ NEXT (3); \
} \
}
@@ -515,7 +520,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Bring the VM to a halt, returning all the values from the stack.
*/
- VM_DEFINE_OP (0, halt, "halt", OP1 (U8_X24))
+ VM_DEFINE_OP (0, halt, "halt", OP1 (X32))
{
/* Boot closure in r0, empty frame in r1/r2, proc in r3, values from r4. */
@@ -553,7 +558,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* stack; the precise number can be had by subtracting the address of
* PROC from the post-call SP.
*/
- VM_DEFINE_OP (1, call, "call", OP2 (U8_U24, X8_U24))
+ VM_DEFINE_OP (1, call, "call", OP2 (X8_F24, X8_C24))
{
scm_t_uint32 proc, nlocals;
union scm_vm_stack_element *old_fp;
@@ -593,7 +598,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* the current IP. Since PROC is not dereferenced, it may be some
* other representation of the closure.
*/
- VM_DEFINE_OP (2, call_label, "call-label", OP3 (U8_U24, X8_U24, L32))
+ VM_DEFINE_OP (2, call_label, "call-label", OP3 (X8_F24, X8_C24, L32))
{
scm_t_uint32 proc, nlocals;
scm_t_int32 label;
@@ -628,7 +633,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* arguments have already been shuffled into position. Will reset the
* frame to NLOCALS.
*/
- VM_DEFINE_OP (3, tail_call, "tail-call", OP1 (U8_U24))
+ VM_DEFINE_OP (3, tail_call, "tail-call", OP1 (X8_C24))
{
scm_t_uint32 nlocals;
@@ -653,7 +658,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Tail-call a known procedure. As call is to call-label, tail-call
* is to tail-call-label.
*/
- VM_DEFINE_OP (4, tail_call_label, "tail-call-label", OP2 (U8_U24, L32))
+ VM_DEFINE_OP (4, tail_call_label, "tail-call-label", OP2 (X8_C24, L32))
{
scm_t_uint32 nlocals;
scm_t_int32 label;
@@ -679,7 +684,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* FROM, shuffled down to start at slot 0. This is part of the
* implementation of the call-with-values builtin.
*/
- VM_DEFINE_OP (5, tail_call_shuffle, "tail-call/shuffle", OP1 (U8_U24))
+ VM_DEFINE_OP (5, tail_call_shuffle, "tail-call/shuffle", OP1 (X8_F24))
{
scm_t_uint32 n, from, nlocals;
@@ -711,7 +716,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* PROC, asserting that the call actually returned at least one
* value. Afterwards, resets the frame to NLOCALS locals.
*/
- VM_DEFINE_OP (6, receive, "receive", OP2 (U8_U12_U12, X8_U24) | OP_DST)
+ VM_DEFINE_OP (6, receive, "receive", OP2 (X8_F12_F12, X8_C24) | OP_DST)
{
scm_t_uint16 dst, proc;
scm_t_uint32 nlocals;
@@ -731,7 +736,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* return values equals NVALUES exactly. After receive-values has
* run, the values can be copied down via `mov'.
*/
- VM_DEFINE_OP (7, receive_values, "receive-values", OP2 (U8_U24, B1_X7_U24))
+ VM_DEFINE_OP (7, receive_values, "receive-values", OP2 (X8_F24, B1_X7_C24))
{
scm_t_uint32 proc, nvalues;
UNPACK_24 (op, proc);
@@ -749,7 +754,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Return a value.
*/
- VM_DEFINE_OP (8, return, "return", OP1 (U8_U24))
+ VM_DEFINE_OP (8, return, "return", OP1 (X8_S24))
{
scm_t_uint32 src;
UNPACK_24 (op, src);
@@ -764,7 +769,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* shuffled down to a contiguous array starting at slot 1.
* We also expect the frame has already been reset.
*/
- VM_DEFINE_OP (9, return_values, "return-values", OP1 (U8_X24))
+ VM_DEFINE_OP (9, return_values, "return-values", OP1 (X32))
{
union scm_vm_stack_element *old_fp;
@@ -798,7 +803,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* calling frame. This instruction is part of the trampolines
* created in gsubr.c, and is not generated by the compiler.
*/
- VM_DEFINE_OP (10, subr_call, "subr-call", OP1 (U8_U24))
+ VM_DEFINE_OP (10, subr_call, "subr-call", OP1 (X8_C24))
{
scm_t_uint32 ptr_idx;
SCM pointer, ret;
@@ -879,7 +884,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* part of the trampolines created by the FFI, and is not generated by
* the compiler.
*/
- VM_DEFINE_OP (11, foreign_call, "foreign-call", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (11, foreign_call, "foreign-call", OP1 (X8_C12_C12))
{
scm_t_uint16 cif_idx, ptr_idx;
SCM closure, cif, pointer, ret;
@@ -913,7 +918,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* the implementation of undelimited continuations, and is not
* generated by the compiler.
*/
- VM_DEFINE_OP (12, continuation_call, "continuation-call", OP1 (U8_U24))
+ VM_DEFINE_OP (12, continuation_call, "continuation-call", OP1 (X8_C24))
{
SCM contregs;
scm_t_uint32 contregs_idx;
@@ -943,7 +948,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* instruction is part of the implementation of partial continuations,
* and is not generated by the compiler.
*/
- VM_DEFINE_OP (13, compose_continuation, "compose-continuation", OP1 (U8_U24))
+ VM_DEFINE_OP (13, compose_continuation, "compose-continuation", OP1 (X8_C24))
{
SCM vmcont;
scm_t_uint32 cont_idx;
@@ -966,7 +971,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* arguments. This instruction is part of the implementation of
* `apply', and is not generated by the compiler.
*/
- VM_DEFINE_OP (14, tail_apply, "tail-apply", OP1 (U8_X24))
+ VM_DEFINE_OP (14, tail_apply, "tail-apply", OP1 (X32))
{
int i, list_idx, list_len, nlocals;
SCM list;
@@ -1012,7 +1017,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* local slot 1 to it. This instruction is part of the implementation
* of `call/cc', and is not generated by the compiler.
*/
- VM_DEFINE_OP (15, call_cc, "call/cc", OP1 (U8_X24))
+ VM_DEFINE_OP (15, call_cc, "call/cc", OP1 (X32))
{
SCM vm_cont, cont;
scm_t_dynstack *dynstack;
@@ -1064,7 +1069,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* of the values in the frame are returned to the prompt handler.
* This corresponds to a tail application of abort-to-prompt.
*/
- VM_DEFINE_OP (16, abort, "abort", OP1 (U8_X24))
+ VM_DEFINE_OP (16, abort, "abort", OP1 (X32))
{
scm_t_uint32 nlocals = FRAME_LOCALS_COUNT ();
@@ -1084,7 +1089,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Load a builtin stub by index into DST.
*/
- VM_DEFINE_OP (17, builtin_ref, "builtin-ref", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (17, builtin_ref, "builtin-ref", OP1 (X8_S12_C12) | OP_DST)
{
scm_t_uint16 dst, idx;
@@ -1109,15 +1114,15 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* than EXPECTED, respectively, add OFFSET, a signed 24-bit number, to
* the current instruction pointer.
*/
- VM_DEFINE_OP (18, br_if_nargs_ne, "br-if-nargs-ne", OP2 (U8_U24, X8_L24))
+ VM_DEFINE_OP (18, br_if_nargs_ne, "br-if-nargs-ne", OP2 (X8_C24, X8_L24))
{
BR_NARGS (!=);
}
- VM_DEFINE_OP (19, br_if_nargs_lt, "br-if-nargs-lt", OP2 (U8_U24, X8_L24))
+ VM_DEFINE_OP (19, br_if_nargs_lt, "br-if-nargs-lt", OP2 (X8_C24, X8_L24))
{
BR_NARGS (<);
}
- VM_DEFINE_OP (20, br_if_nargs_gt, "br-if-nargs-gt", OP2 (U8_U24, X8_L24))
+ VM_DEFINE_OP (20, br_if_nargs_gt, "br-if-nargs-gt", OP2 (X8_C24, X8_L24))
{
BR_NARGS (>);
}
@@ -1129,7 +1134,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the number of actual arguments is not ==, >=, or <= EXPECTED,
* respectively, signal an error.
*/
- VM_DEFINE_OP (21, assert_nargs_ee, "assert-nargs-ee", OP1 (U8_U24))
+ VM_DEFINE_OP (21, assert_nargs_ee, "assert-nargs-ee", OP1 (X8_C24))
{
scm_t_uint32 expected;
UNPACK_24 (op, expected);
@@ -1137,7 +1142,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
vm_error_wrong_num_args (LOCAL_REF (0)));
NEXT (1);
}
- VM_DEFINE_OP (22, assert_nargs_ge, "assert-nargs-ge", OP1 (U8_U24))
+ VM_DEFINE_OP (22, assert_nargs_ge, "assert-nargs-ge", OP1 (X8_C24))
{
scm_t_uint32 expected;
UNPACK_24 (op, expected);
@@ -1145,7 +1150,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
vm_error_wrong_num_args (LOCAL_REF (0)));
NEXT (1);
}
- VM_DEFINE_OP (23, assert_nargs_le, "assert-nargs-le", OP1 (U8_U24))
+ VM_DEFINE_OP (23, assert_nargs_le, "assert-nargs-le", OP1 (X8_C24))
{
scm_t_uint32 expected;
UNPACK_24 (op, expected);
@@ -1160,7 +1165,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* setting them all to SCM_UNDEFINED, except those nargs values that
* were passed as arguments and procedure.
*/
- VM_DEFINE_OP (24, alloc_frame, "alloc-frame", OP1 (U8_U24))
+ VM_DEFINE_OP (24, alloc_frame, "alloc-frame", OP1 (X8_C24))
{
scm_t_uint32 nlocals, nargs;
UNPACK_24 (op, nlocals);
@@ -1179,7 +1184,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Used to reset the frame size to something less than the size that
* was previously set via alloc-frame.
*/
- VM_DEFINE_OP (25, reset_frame, "reset-frame", OP1 (U8_U24))
+ VM_DEFINE_OP (25, reset_frame, "reset-frame", OP1 (X8_C24))
{
scm_t_uint32 nlocals;
UNPACK_24 (op, nlocals);
@@ -1187,12 +1192,57 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
NEXT (1);
}
+ /* push src:24
+ *
+ * Push SRC onto the stack.
+ */
+ VM_DEFINE_OP (26, push, "push", OP1 (X8_S24))
+ {
+ scm_t_uint32 src;
+ SCM val;
+
+ UNPACK_24 (op, src);
+ val = SP_REF (src);
+ ALLOC_FRAME (FRAME_LOCALS_COUNT () + 1);
+ SP_SET (0, val);
+ NEXT (1);
+ }
+
+ /* pop dst:24
+ *
+ * Pop the stack, storing to DST.
+ */
+ VM_DEFINE_OP (27, pop, "pop", OP1 (X8_S24) | OP_DST)
+ {
+ scm_t_uint32 dst;
+ SCM val;
+
+ UNPACK_24 (op, dst);
+ val = SP_REF (0);
+ vp->sp++;
+ SP_SET (dst, val);
+ NEXT (1);
+ }
+
+ /* drop count:24
+ *
+ * Drop some number of values from the stack.
+ */
+ VM_DEFINE_OP (28, drop, "drop", OP1 (X8_C24))
+ {
+ scm_t_uint32 count;
+
+ UNPACK_24 (op, count);
+ vp->sp += count;
+ NEXT (1);
+ }
+
/* assert-nargs-ee/locals expected:12 nlocals:12
*
* Equivalent to a sequence of assert-nargs-ee and reserve-locals. The
* number of locals reserved is EXPECTED + NLOCALS.
*/
- VM_DEFINE_OP (26, assert_nargs_ee_locals, "assert-nargs-ee/locals", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (29, assert_nargs_ee_locals, "assert-nargs-ee/locals", OP1 (X8_C12_C12))
{
scm_t_uint16 expected, nlocals;
UNPACK_12_12 (op, expected, nlocals);
@@ -1215,7 +1265,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* See "Case-lambda" in the manual, for more on how case-lambda
* chooses the clause to apply.
*/
- VM_DEFINE_OP (27, br_if_npos_gt, "br-if-npos-gt", OP3 (U8_U24, X8_U24, X8_L24))
+ VM_DEFINE_OP (30, br_if_npos_gt, "br-if-npos-gt", OP3 (X8_C24, X8_C24, X8_L24))
{
scm_t_uint32 nreq, npos;
@@ -1253,7 +1303,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* A macro-mega-instruction.
*/
- VM_DEFINE_OP (28, bind_kwargs, "bind-kwargs", OP4 (U8_U24, U8_U24, X8_U24, N32))
+ VM_DEFINE_OP (31, bind_kwargs, "bind-kwargs", OP4 (X8_C24, C8_C24, X8_C24, N32))
{
scm_t_uint32 nreq, nreq_and_opt, ntotal, npositional, nkw, n, nargs;
scm_t_int32 kw_offset;
@@ -1339,7 +1389,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Collect any arguments at or above DST into a list, and store that
* list at DST.
*/
- VM_DEFINE_OP (29, bind_rest, "bind-rest", OP1 (U8_U24) | OP_DST)
+ VM_DEFINE_OP (32, bind_rest, "bind-rest", OP1 (X8_F24) | OP_DST)
{
scm_t_uint32 dst, nargs;
SCM rest = SCM_EOL;
@@ -1381,7 +1431,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Add OFFSET, a signed 24-bit number, to the current instruction
* pointer.
*/
- VM_DEFINE_OP (30, br, "br", OP1 (U8_L24))
+ VM_DEFINE_OP (33, br, "br", OP1 (X8_L24))
{
scm_t_int32 offset = op;
offset >>= 8; /* Sign-extending shift. */
@@ -1395,7 +1445,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is true for the purposes of Scheme, add
* OFFSET, a signed 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (31, br_if_true, "br-if-true", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (34, br_if_true, "br-if-true", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, scm_is_true (x));
}
@@ -1405,7 +1455,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is the end-of-list or Lisp nil, add OFFSET, a
* signed 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (32, br_if_null, "br-if-null", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (35, br_if_null, "br-if-null", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, scm_is_null (x));
}
@@ -1415,7 +1465,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is false to Lisp, add OFFSET, a signed 24-bit
* number, to the current instruction pointer.
*/
- VM_DEFINE_OP (33, br_if_nil, "br-if-nil", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (36, br_if_nil, "br-if-nil", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, scm_is_lisp_false (x));
}
@@ -1425,7 +1475,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is a pair, add OFFSET, a signed 24-bit number,
* to the current instruction pointer.
*/
- VM_DEFINE_OP (34, br_if_pair, "br-if-pair", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (37, br_if_pair, "br-if-pair", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, scm_is_pair (x));
}
@@ -1435,7 +1485,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is a struct, add OFFSET, a signed 24-bit
* number, to the current instruction pointer.
*/
- VM_DEFINE_OP (35, br_if_struct, "br-if-struct", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (38, br_if_struct, "br-if-struct", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, SCM_STRUCTP (x));
}
@@ -1445,7 +1495,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST is a char, add OFFSET, a signed 24-bit number,
* to the current instruction pointer.
*/
- VM_DEFINE_OP (36, br_if_char, "br-if-char", OP2 (U8_U24, B1_X7_L24))
+ VM_DEFINE_OP (39, br_if_char, "br-if-char", OP2 (X8_S24, B1_X7_L24))
{
BR_UNARY (x, SCM_CHARP (x));
}
@@ -1455,7 +1505,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in TEST has the TC7 given in the second word, add
* OFFSET, a signed 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (37, br_if_tc7, "br-if-tc7", OP2 (U8_U24, B1_U7_L24))
+ VM_DEFINE_OP (40, br_if_tc7, "br-if-tc7", OP2 (X8_S24, B1_C7_L24))
{
BR_UNARY (x, SCM_HAS_TYP7 (x, (ip[1] >> 1) & 0x7f));
}
@@ -1465,7 +1515,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is eq? to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (38, br_if_eq, "br-if-eq", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (41, br_if_eq, "br-if-eq", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_BINARY (x, y, scm_is_eq (x, y));
}
@@ -1475,7 +1525,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is eqv? to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (39, br_if_eqv, "br-if-eqv", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (42, br_if_eqv, "br-if-eqv", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_BINARY (x, y,
scm_is_eq (x, y)
@@ -1491,7 +1541,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*/
// FIXME: Should sync_ip before calling out and cache_locals before coming
// back! Another reason to remove this opcode!
- VM_DEFINE_OP (40, br_if_equal, "br-if-equal", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (43, br_if_equal, "br-if-equal", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_BINARY (x, y,
scm_is_eq (x, y)
@@ -1499,12 +1549,26 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
&& scm_is_true (scm_equal_p (x, y))));
}
+ /* br-if-logtest a:24 _:8 b:24 invert:1 _:7 offset:24
+ *
+ * If the exact integer in A has any bits in common with the exact
+ * integer in B, add OFFSET, a signed 24-bit number, to the current
+ * instruction pointer.
+ */
+ VM_DEFINE_OP (44, br_if_logtest, "br-if-logtest", OP3 (X8_S24, X8_S24, B1_X7_L24))
+ {
+ BR_BINARY (x, y,
+ ((SCM_I_INUMP (x) && SCM_I_INUMP (y))
+ ? (SCM_UNPACK (x) & SCM_UNPACK (y) & ~scm_tc2_int)
+ : scm_is_true (scm_logtest (x, y))));
+ }
+
/* br-if-= a:12 b:12 invert:1 _:7 offset:24
*
* If the value in A is = to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (41, br_if_ee, "br-if-=", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (45, br_if_ee, "br-if-=", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_ARITHMETIC (==, scm_num_eq_p);
}
@@ -1514,7 +1578,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is < to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (42, br_if_lt, "br-if-<", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (46, br_if_lt, "br-if-<", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_ARITHMETIC (<, scm_less_p);
}
@@ -1524,7 +1588,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is <= to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- VM_DEFINE_OP (43, br_if_le, "br-if-<=", OP2 (U8_U12_U12, B1_X7_L24))
+ VM_DEFINE_OP (47, br_if_le, "br-if-<=", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
BR_ARITHMETIC (<=, scm_leq_p);
}
@@ -1540,7 +1604,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Copy a value from one local slot to another.
*/
- VM_DEFINE_OP (44, mov, "mov", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (48, mov, "mov", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst;
scm_t_uint16 src;
@@ -1555,7 +1619,24 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Copy a value from one local slot to another.
*/
- VM_DEFINE_OP (45, long_mov, "long-mov", OP2 (U8_U24, X8_U24) | OP_DST)
+ VM_DEFINE_OP (49, long_mov, "long-mov", OP2 (X8_S24, X8_S24) | OP_DST)
+ {
+ scm_t_uint32 dst;
+ scm_t_uint32 src;
+
+ UNPACK_24 (op, dst);
+ UNPACK_24 (ip[1], src);
+ LOCAL_SET (dst, LOCAL_REF (src));
+
+ NEXT (2);
+ }
+
+ /* long-fmov dst:24 _:8 src:24
+ *
+ * Copy a value from one local slot to another. Slot indexes are
+ * relative to the FP.
+ */
+ VM_DEFINE_OP (50, long_fmov, "long-fmov", OP2 (X8_F24, X8_F24) | OP_DST)
{
scm_t_uint32 dst;
scm_t_uint32 src;
@@ -1571,7 +1652,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Create a new variable holding SRC, and place it in DST.
*/
- VM_DEFINE_OP (46, box, "box", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (51, box, "box", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
UNPACK_12_12 (op, dst, src);
@@ -1585,7 +1666,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Unpack the variable at SRC into DST, asserting that the variable is
* actually bound.
*/
- VM_DEFINE_OP (47, box_ref, "box-ref", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (52, box_ref, "box-ref", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
SCM var;
@@ -1602,7 +1683,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Set the contents of the variable at DST to SET.
*/
- VM_DEFINE_OP (48, box_set, "box-set!", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (53, box_set, "box-set!", OP1 (X8_S12_S12))
{
scm_t_uint16 dst, src;
SCM var;
@@ -1621,7 +1702,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* signed 32-bit integer. Space for NFREE free variables will be
* allocated.
*/
- VM_DEFINE_OP (49, make_closure, "make-closure", OP3 (U8_U24, L32, X8_U24) | OP_DST)
+ VM_DEFINE_OP (54, make_closure, "make-closure", OP3 (X8_S24, L32, X8_C24) | OP_DST)
{
scm_t_uint32 dst, nfree, n;
scm_t_int32 offset;
@@ -1646,7 +1727,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Load free variable IDX from the closure SRC into local slot DST.
*/
- VM_DEFINE_OP (50, free_ref, "free-ref", OP2 (U8_U12_U12, X8_U24) | OP_DST)
+ VM_DEFINE_OP (55, free_ref, "free-ref", OP2 (X8_S12_S12, X8_C24) | OP_DST)
{
scm_t_uint16 dst, src;
scm_t_uint32 idx;
@@ -1661,7 +1742,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Set free variable IDX from the closure DST to SRC.
*/
- VM_DEFINE_OP (51, free_set, "free-set!", OP2 (U8_U12_U12, X8_U24))
+ VM_DEFINE_OP (56, free_set, "free-set!", OP2 (X8_S12_S12, X8_C24))
{
scm_t_uint16 dst, src;
scm_t_uint32 idx;
@@ -1684,7 +1765,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Make an immediate whose low bits are LOW-BITS, and whose top bits are
* 0.
*/
- VM_DEFINE_OP (52, make_short_immediate, "make-short-immediate", OP1 (U8_U8_I16) | OP_DST)
+ VM_DEFINE_OP (57, make_short_immediate, "make-short-immediate", OP1 (X8_S8_I16) | OP_DST)
{
scm_t_uint8 dst;
scm_t_bits val;
@@ -1699,7 +1780,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Make an immediate whose low bits are LOW-BITS, and whose top bits are
* 0.
*/
- VM_DEFINE_OP (53, make_long_immediate, "make-long-immediate", OP2 (U8_U24, I32) | OP_DST)
+ VM_DEFINE_OP (58, make_long_immediate, "make-long-immediate", OP2 (X8_S24, I32) | OP_DST)
{
scm_t_uint32 dst;
scm_t_bits val;
@@ -1714,7 +1795,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Make an immediate with HIGH-BITS and LOW-BITS.
*/
- VM_DEFINE_OP (54, make_long_long_immediate, "make-long-long-immediate", OP3 (U8_U24, A32, B32) | OP_DST)
+ VM_DEFINE_OP (59, make_long_long_immediate, "make-long-long-immediate", OP3 (X8_S24, A32, B32) | OP_DST)
{
scm_t_uint32 dst;
scm_t_bits val;
@@ -1745,7 +1826,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Whether the object is mutable or immutable depends on where it was
* allocated by the compiler, and loaded by the loader.
*/
- VM_DEFINE_OP (55, make_non_immediate, "make-non-immediate", OP2 (U8_U24, N32) | OP_DST)
+ VM_DEFINE_OP (60, make_non_immediate, "make-non-immediate", OP2 (X8_S24, N32) | OP_DST)
{
scm_t_uint32 dst;
scm_t_int32 offset;
@@ -1774,7 +1855,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* that the compiler is unable to statically allocate, like symbols.
* These values would be initialized when the object file loads.
*/
- VM_DEFINE_OP (56, static_ref, "static-ref", OP2 (U8_U24, S32) | OP_DST)
+ VM_DEFINE_OP (61, static_ref, "static-ref", OP2 (X8_S24, R32) | OP_DST)
{
scm_t_uint32 dst;
scm_t_int32 offset;
@@ -1797,7 +1878,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Store a SCM value into memory, OFFSET 32-bit words away from the
* current instruction pointer. OFFSET is a signed value.
*/
- VM_DEFINE_OP (57, static_set, "static-set!", OP2 (U8_U24, LO32))
+ VM_DEFINE_OP (62, static_set, "static-set!", OP2 (X8_S24, LO32))
{
scm_t_uint32 src;
scm_t_int32 offset;
@@ -1819,7 +1900,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* are signed 32-bit values, indicating a memory address as a number
* of 32-bit words away from the current instruction pointer.
*/
- VM_DEFINE_OP (58, static_patch, "static-patch!", OP3 (U8_X24, LO32, L32))
+ VM_DEFINE_OP (63, static_patch, "static-patch!", OP3 (X32, LO32, L32))
{
scm_t_int32 dst_offset, src_offset;
void *src;
@@ -1877,7 +1958,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store the current module in DST.
*/
- VM_DEFINE_OP (59, current_module, "current-module", OP1 (U8_U24) | OP_DST)
+ VM_DEFINE_OP (64, current_module, "current-module", OP1 (X8_S24) | OP_DST)
{
scm_t_uint32 dst;
@@ -1894,7 +1975,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Resolve SYM in the current module, and place the resulting variable
* in DST.
*/
- VM_DEFINE_OP (60, resolve, "resolve", OP2 (U8_U24, B1_X7_U24) | OP_DST)
+ VM_DEFINE_OP (65, resolve, "resolve", OP2 (X8_S24, B1_X7_S24) | OP_DST)
{
scm_t_uint32 dst;
scm_t_uint32 sym;
@@ -1918,7 +1999,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Look up a binding for SYM in the current module, creating it if
* necessary. Set its value to VAL.
*/
- VM_DEFINE_OP (61, define, "define!", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (66, define, "define!", OP1 (X8_S12_S12))
{
scm_t_uint16 sym, val;
UNPACK_12_12 (op, sym, val);
@@ -1947,7 +2028,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* DST, and caching the resolved variable so that we will hit the cache next
* time.
*/
- VM_DEFINE_OP (62, toplevel_box, "toplevel-box", OP5 (U8_U24, S32, S32, N32, B1_X31) | OP_DST)
+ VM_DEFINE_OP (67, toplevel_box, "toplevel-box", OP5 (X8_S24, R32, R32, N32, B1_X31) | OP_DST)
{
scm_t_uint32 dst;
scm_t_int32 var_offset;
@@ -2000,7 +2081,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Like toplevel-box, except MOD-OFFSET points at the name of a module
* instead of the module itself.
*/
- VM_DEFINE_OP (63, module_box, "module-box", OP5 (U8_U24, S32, N32, N32, B1_X31) | OP_DST)
+ VM_DEFINE_OP (68, module_box, "module-box", OP5 (X8_S24, R32, N32, N32, B1_X31) | OP_DST)
{
scm_t_uint32 dst;
scm_t_int32 var_offset;
@@ -2070,7 +2151,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* will expect a multiple-value return as if from a call with the
* procedure at PROC-SLOT.
*/
- VM_DEFINE_OP (64, prompt, "prompt", OP3 (U8_U24, B1_X7_U24, X8_L24))
+ VM_DEFINE_OP (69, prompt, "prompt", OP3 (X8_S24, B1_X7_F24, X8_L24))
{
scm_t_uint32 tag, proc_slot;
scm_t_int32 offset;
@@ -2102,7 +2183,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* the compiler should have inserted checks that they wind and unwind
* procs are thunks, if it could not prove that to be the case.
*/
- VM_DEFINE_OP (65, wind, "wind", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (70, wind, "wind", OP1 (X8_S12_S12))
{
scm_t_uint16 winder, unwinder;
UNPACK_12_12 (op, winder, unwinder);
@@ -2116,7 +2197,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* A normal exit from the dynamic extent of an expression. Pop the top
* entry off of the dynamic stack.
*/
- VM_DEFINE_OP (66, unwind, "unwind", OP1 (U8_X24))
+ VM_DEFINE_OP (71, unwind, "unwind", OP1 (X32))
{
scm_dynstack_pop (&thread->dynstack);
NEXT (1);
@@ -2126,7 +2207,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Dynamically bind VALUE to FLUID.
*/
- VM_DEFINE_OP (67, push_fluid, "push-fluid", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (72, push_fluid, "push-fluid", OP1 (X8_S12_S12))
{
scm_t_uint32 fluid, value;
@@ -2143,7 +2224,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Leave the dynamic extent of a with-fluid* expression, restoring the
* fluid to its previous value.
*/
- VM_DEFINE_OP (68, pop_fluid, "pop-fluid", OP1 (U8_X24))
+ VM_DEFINE_OP (73, pop_fluid, "pop-fluid", OP1 (X32))
{
/* This function must not allocate. */
scm_dynstack_unwind_fluid (&thread->dynstack,
@@ -2155,7 +2236,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Reference the fluid in SRC, and place the value in DST.
*/
- VM_DEFINE_OP (69, fluid_ref, "fluid-ref", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (74, fluid_ref, "fluid-ref", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
size_t num;
@@ -2188,7 +2269,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Set the value of the fluid in DST to the value in SRC.
*/
- VM_DEFINE_OP (70, fluid_set, "fluid-set", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (75, fluid_set, "fluid-set", OP1 (X8_S12_S12))
{
scm_t_uint16 a, b;
size_t num;
@@ -2221,7 +2302,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store the length of the string in SRC in DST.
*/
- VM_DEFINE_OP (71, string_length, "string-length", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (76, string_length, "string-length", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (str);
if (SCM_LIKELY (scm_is_string (str)))
@@ -2238,7 +2319,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Fetch the character at position IDX in the string in SRC, and store
* it in DST.
*/
- VM_DEFINE_OP (72, string_ref, "string-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (77, string_ref, "string-ref", OP1 (X8_S8_S8_S8) | OP_DST)
{
scm_t_signed_bits i = 0;
ARGS2 (str, idx);
@@ -2260,7 +2341,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Parse a string in SRC to a number, and store in DST.
*/
- VM_DEFINE_OP (73, string_to_number, "string->number", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (78, string_to_number, "string->number", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
@@ -2276,7 +2357,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Parse a string in SRC to a symbol, and store in DST.
*/
- VM_DEFINE_OP (74, string_to_symbol, "string->symbol", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (79, string_to_symbol, "string->symbol", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
@@ -2290,7 +2371,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Make a keyword from the symbol in SRC, and store it in DST.
*/
- VM_DEFINE_OP (75, symbol_to_keyword, "symbol->keyword", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (80, symbol_to_keyword, "symbol->keyword", OP1 (X8_S12_S12) | OP_DST)
{
scm_t_uint16 dst, src;
UNPACK_12_12 (op, dst, src);
@@ -2309,7 +2390,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Cons CAR and CDR, and store the result in DST.
*/
- VM_DEFINE_OP (76, cons, "cons", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (81, cons, "cons", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN (scm_inline_cons (thread, x, y));
@@ -2319,7 +2400,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the car of SRC in DST.
*/
- VM_DEFINE_OP (77, car, "car", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (82, car, "car", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (x);
VM_VALIDATE_PAIR (x, "car");
@@ -2330,7 +2411,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the cdr of SRC in DST.
*/
- VM_DEFINE_OP (78, cdr, "cdr", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (83, cdr, "cdr", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (x);
VM_VALIDATE_PAIR (x, "cdr");
@@ -2341,7 +2422,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Set the car of DST to SRC.
*/
- VM_DEFINE_OP (79, set_car, "set-car!", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (84, set_car, "set-car!", OP1 (X8_S12_S12))
{
scm_t_uint16 a, b;
SCM x, y;
@@ -2357,7 +2438,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Set the cdr of DST to SRC.
*/
- VM_DEFINE_OP (80, set_cdr, "set-cdr!", OP1 (U8_U12_U12))
+ VM_DEFINE_OP (85, set_cdr, "set-cdr!", OP1 (X8_S12_S12))
{
scm_t_uint16 a, b;
SCM x, y;
@@ -2380,7 +2461,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Add A to B, and place the result in DST.
*/
- VM_DEFINE_OP (81, add, "add", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (86, add, "add", OP1 (X8_S8_S8_S8) | OP_DST)
{
BINARY_INTEGER_OP (+, scm_sum);
}
@@ -2389,7 +2470,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Add 1 to the value in SRC, and place the result in DST.
*/
- VM_DEFINE_OP (82, add1, "add1", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (87, add1, "add1", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (x);
@@ -2413,7 +2494,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Subtract B from A, and place the result in DST.
*/
- VM_DEFINE_OP (83, sub, "sub", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (88, sub, "sub", OP1 (X8_S8_S8_S8) | OP_DST)
{
BINARY_INTEGER_OP (-, scm_difference);
}
@@ -2422,7 +2503,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Subtract 1 from SRC, and place the result in DST.
*/
- VM_DEFINE_OP (84, sub1, "sub1", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (89, sub1, "sub1", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (x);
@@ -2446,7 +2527,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Multiply A and B, and place the result in DST.
*/
- VM_DEFINE_OP (85, mul, "mul", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (90, mul, "mul", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN_EXP (scm_product (x, y));
@@ -2456,7 +2537,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Divide A by B, and place the result in DST.
*/
- VM_DEFINE_OP (86, div, "div", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (91, div, "div", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN_EXP (scm_divide (x, y));
@@ -2466,7 +2547,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Divide A by B, and place the quotient in DST.
*/
- VM_DEFINE_OP (87, quo, "quo", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (92, quo, "quo", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN_EXP (scm_quotient (x, y));
@@ -2476,7 +2557,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Divide A by B, and place the remainder in DST.
*/
- VM_DEFINE_OP (88, rem, "rem", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (93, rem, "rem", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN_EXP (scm_remainder (x, y));
@@ -2486,7 +2567,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the modulo of A by B in DST.
*/
- VM_DEFINE_OP (89, mod, "mod", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (94, mod, "mod", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
RETURN_EXP (scm_modulo (x, y));
@@ -2496,7 +2577,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Shift A arithmetically by B bits, and place the result in DST.
*/
- VM_DEFINE_OP (90, ash, "ash", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (95, ash, "ash", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
if (SCM_I_INUMP (x) && SCM_I_INUMP (y))
@@ -2533,7 +2614,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the bitwise AND of A and B into DST.
*/
- VM_DEFINE_OP (91, logand, "logand", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (96, logand, "logand", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
if (SCM_I_INUMP (x) && SCM_I_INUMP (y))
@@ -2546,7 +2627,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the bitwise inclusive OR of A with B in DST.
*/
- VM_DEFINE_OP (92, logior, "logior", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (97, logior, "logior", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
if (SCM_I_INUMP (x) && SCM_I_INUMP (y))
@@ -2559,7 +2640,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Place the bitwise exclusive OR of A with B in DST.
*/
- VM_DEFINE_OP (93, logxor, "logxor", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (98, logxor, "logxor", OP1 (X8_S8_S8_S8) | OP_DST)
{
ARGS2 (x, y);
if (SCM_I_INUMP (x) && SCM_I_INUMP (y))
@@ -2572,7 +2653,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Make a vector and write it to DST. The vector will have space for
* LENGTH slots. They will be filled with the value in slot INIT.
*/
- VM_DEFINE_OP (94, make_vector, "make-vector", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (99, make_vector, "make-vector", OP1 (X8_S8_S8_S8) | OP_DST)
{
scm_t_uint8 dst, init, length;
@@ -2589,7 +2670,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* will have space for LENGTH slots, an immediate value. They will be
* filled with the value in slot INIT.
*/
- VM_DEFINE_OP (95, make_vector_immediate, "make-vector/immediate", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (100, make_vector_immediate, "make-vector/immediate", OP1 (X8_S8_C8_S8) | OP_DST)
{
scm_t_uint8 dst, init;
scm_t_int32 length, n;
@@ -2610,7 +2691,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store the length of the vector in SRC in DST.
*/
- VM_DEFINE_OP (96, vector_length, "vector-length", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (101, vector_length, "vector-length", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (vect);
VM_ASSERT (SCM_I_IS_VECTOR (vect),
@@ -2623,7 +2704,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Fetch the item at position IDX in the vector in SRC, and store it
* in DST.
*/
- VM_DEFINE_OP (97, vector_ref, "vector-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (102, vector_ref, "vector-ref", OP1 (X8_S8_S8_S8) | OP_DST)
{
scm_t_signed_bits i = 0;
ARGS2 (vect, idx);
@@ -2641,7 +2722,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Fill DST with the item IDX elements into the vector at SRC. Useful
* for building data types using vectors.
*/
- VM_DEFINE_OP (98, vector_ref_immediate, "vector-ref/immediate", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (103, vector_ref_immediate, "vector-ref/immediate", OP1 (X8_S8_S8_C8) | OP_DST)
{
scm_t_uint8 dst, src, idx;
SCM v;
@@ -2660,7 +2741,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store SRC into the vector DST at index IDX.
*/
- VM_DEFINE_OP (99, vector_set, "vector-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (104, vector_set, "vector-set!", OP1 (X8_S8_S8_S8))
{
scm_t_uint8 dst, idx_var, src;
SCM vect, idx, val;
@@ -2686,7 +2767,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Store SRC into the vector DST at index IDX. Here IDX is an
* immediate value.
*/
- VM_DEFINE_OP (100, vector_set_immediate, "vector-set!/immediate", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (105, vector_set_immediate, "vector-set!/immediate", OP1 (X8_S8_C8_S8))
{
scm_t_uint8 dst, idx, src;
SCM vect, val;
@@ -2714,20 +2795,105 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store the vtable of SRC into DST.
*/
- VM_DEFINE_OP (101, struct_vtable, "struct-vtable", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (106, struct_vtable, "struct-vtable", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (obj);
VM_VALIDATE_STRUCT (obj, "struct_vtable");
RETURN (SCM_STRUCT_VTABLE (obj));
}
+ /* allocate-struct dst:8 vtable:8 nfields:8
+ *
+ * Allocate a new struct with VTABLE, and place it in DST. The struct
+ * will be constructed with space for NFIELDS fields, which should
+ * correspond to the field count of the VTABLE.
+ */
+ VM_DEFINE_OP (107, allocate_struct, "allocate-struct", OP1 (X8_S8_S8_S8) | OP_DST)
+ {
+ scm_t_uint8 dst, vtable, nfields;
+ SCM ret;
+
+ UNPACK_8_8_8 (op, dst, vtable, nfields);
+
+ SYNC_IP ();
+ ret = scm_allocate_struct (LOCAL_REF (vtable), LOCAL_REF (nfields));
+ LOCAL_SET (dst, ret);
+
+ NEXT (1);
+ }
+
+ /* struct-ref dst:8 src:8 idx:8
+ *
+ * Fetch the item at slot IDX in the struct in SRC, and store it
+ * in DST.
+ */
+ VM_DEFINE_OP (108, struct_ref, "struct-ref", OP1 (X8_S8_S8_S8) | OP_DST)
+ {
+ scm_t_uint8 dst, src, idx;
+ SCM obj;
+ SCM index;
+
+ UNPACK_8_8_8 (op, dst, src, idx);
+
+ obj = LOCAL_REF (src);
+ index = LOCAL_REF (idx);
+
+ if (SCM_LIKELY (SCM_STRUCTP (obj)
+ && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
+ SCM_VTABLE_FLAG_SIMPLE)
+ && SCM_I_INUMP (index)
+ && SCM_I_INUM (index) >= 0
+ && SCM_I_INUM (index) < (SCM_STRUCT_DATA_REF
+ (SCM_STRUCT_VTABLE (obj),
+ scm_vtable_index_size))))
+ RETURN (SCM_STRUCT_SLOT_REF (obj, SCM_I_INUM (index)));
+
+ SYNC_IP ();
+ RETURN (scm_struct_ref (obj, index));
+ }
+
+ /* struct-set! dst:8 idx:8 src:8
+ *
+ * Store SRC into the struct DST at slot IDX.
+ */
+ VM_DEFINE_OP (109, struct_set, "struct-set!", OP1 (X8_S8_S8_S8))
+ {
+ scm_t_uint8 dst, idx, src;
+ SCM obj, val, index;
+
+ UNPACK_8_8_8 (op, dst, idx, src);
+
+ obj = LOCAL_REF (dst);
+ val = LOCAL_REF (src);
+ index = LOCAL_REF (idx);
+
+ if (SCM_LIKELY (SCM_STRUCTP (obj)
+ && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
+ SCM_VTABLE_FLAG_SIMPLE)
+ && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
+ SCM_VTABLE_FLAG_SIMPLE_RW)
+ && SCM_I_INUMP (index)
+ && SCM_I_INUM (index) >= 0
+ && SCM_I_INUM (index) < (SCM_STRUCT_DATA_REF
+ (SCM_STRUCT_VTABLE (obj),
+ scm_vtable_index_size))))
+ {
+ SCM_STRUCT_SLOT_SET (obj, SCM_I_INUM (index), val);
+ NEXT (1);
+ }
+
+ SYNC_IP ();
+ scm_struct_set_x (obj, index, val);
+ NEXT (1);
+ }
+
/* allocate-struct/immediate dst:8 vtable:8 nfields:8
*
* Allocate a new struct with VTABLE, and place it in DST. The struct
* will be constructed with space for NFIELDS fields, which should
* correspond to the field count of the VTABLE.
*/
- VM_DEFINE_OP (102, allocate_struct_immediate, "allocate-struct/immediate", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (110, allocate_struct_immediate, "allocate-struct/immediate", OP1 (X8_S8_S8_C8) | OP_DST)
{
scm_t_uint8 dst, vtable, nfields;
SCM ret;
@@ -2746,7 +2912,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Fetch the item at slot IDX in the struct in SRC, and store it
* in DST. IDX is an immediate unsigned 8-bit value.
*/
- VM_DEFINE_OP (103, struct_ref_immediate, "struct-ref/immediate", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (111, struct_ref_immediate, "struct-ref/immediate", OP1 (X8_S8_S8_C8) | OP_DST)
{
scm_t_uint8 dst, src, idx;
SCM obj;
@@ -2771,7 +2937,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Store SRC into the struct DST at slot IDX. IDX is an immediate
* unsigned 8-bit value.
*/
- VM_DEFINE_OP (104, struct_set_immediate, "struct-set!/immediate", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (112, struct_set_immediate, "struct-set!/immediate", OP1 (X8_S8_C8_S8))
{
scm_t_uint8 dst, idx, src;
SCM obj, val;
@@ -2802,7 +2968,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*
* Store the vtable of SRC into DST.
*/
- VM_DEFINE_OP (105, class_of, "class-of", OP1 (U8_U12_U12) | OP_DST)
+ VM_DEFINE_OP (113, class_of, "class-of", OP1 (X8_S12_S12) | OP_DST)
{
ARGS1 (obj);
if (SCM_INSTANCEP (obj))
@@ -2817,41 +2983,45 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Arrays, packed uniform arrays, and bytevectors.
*/
- /* load-typed-array dst:8 type:8 shape:8 offset:32 len:32
+ /* load-typed-array dst:24 _:8 type:24 _:8 shape:24 offset:32 len:32
*
* Load the contiguous typed array located at OFFSET 32-bit words away
* from the instruction pointer, and store into DST. LEN is a byte
* length. OFFSET is signed.
*/
- VM_DEFINE_OP (106, load_typed_array, "load-typed-array", OP3 (U8_U8_U8_U8, N32, U32) | OP_DST)
+ VM_DEFINE_OP (114, load_typed_array, "load-typed-array", OP5 (X8_S24, X8_S24, X8_S24, N32, C32) | OP_DST)
{
- scm_t_uint8 dst, type, shape;
+ scm_t_uint32 dst, type, shape;
scm_t_int32 offset;
scm_t_uint32 len;
- UNPACK_8_8_8 (op, dst, type, shape);
- offset = ip[1];
- len = ip[2];
+ UNPACK_24 (op, dst);
+ UNPACK_24 (ip[1], type);
+ UNPACK_24 (ip[2], shape);
+ offset = ip[3];
+ len = ip[4];
SYNC_IP ();
LOCAL_SET (dst, scm_from_contiguous_typed_array (LOCAL_REF (type),
LOCAL_REF (shape),
ip + offset, len));
- NEXT (3);
+ NEXT (5);
}
- /* make-array dst:8 type:8 fill:8 _:8 bounds:24
+ /* make-array dst:24 _:8 type:24 _:8 fill:24 _:8 bounds:24
*
* Make a new array with TYPE, FILL, and BOUNDS, storing it in DST.
*/
- VM_DEFINE_OP (107, make_array, "make-array", OP2 (U8_U8_U8_U8, X8_U24) | OP_DST)
+ VM_DEFINE_OP (115, make_array, "make-array", OP4 (X8_S24, X8_S24, X8_S24, X8_S24) | OP_DST)
{
- scm_t_uint8 dst, type, fill, bounds;
- UNPACK_8_8_8 (op, dst, type, fill);
- UNPACK_24 (ip[1], bounds);
+ scm_t_uint32 dst, type, fill, bounds;
+ UNPACK_24 (op, dst);
+ UNPACK_24 (ip[1], type);
+ UNPACK_24 (ip[2], fill);
+ UNPACK_24 (ip[3], bounds);
SYNC_IP ();
LOCAL_SET (dst, scm_make_typed_array (LOCAL_REF (type), LOCAL_REF (fill),
LOCAL_REF (bounds)));
- NEXT (2);
+ NEXT (4);
}
/* bv-u8-ref dst:8 src:8 idx:8
@@ -2941,42 +3111,42 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
RETURN (scm_bytevector_ ## fn_stem ## _native_ref (bv, idx)); \
} while (0)
- VM_DEFINE_OP (108, bv_u8_ref, "bv-u8-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (116, bv_u8_ref, "bv-u8-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FIXABLE_INT_REF (u8, u8, uint8, 1);
- VM_DEFINE_OP (109, bv_s8_ref, "bv-s8-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (117, bv_s8_ref, "bv-s8-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FIXABLE_INT_REF (s8, s8, int8, 1);
- VM_DEFINE_OP (110, bv_u16_ref, "bv-u16-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (118, bv_u16_ref, "bv-u16-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FIXABLE_INT_REF (u16, u16_native, uint16, 2);
- VM_DEFINE_OP (111, bv_s16_ref, "bv-s16-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (119, bv_s16_ref, "bv-s16-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FIXABLE_INT_REF (s16, s16_native, int16, 2);
- VM_DEFINE_OP (112, bv_u32_ref, "bv-u32-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (120, bv_u32_ref, "bv-u32-ref", OP1 (X8_S8_S8_S8) | OP_DST)
#if SIZEOF_VOID_P > 4
BV_FIXABLE_INT_REF (u32, u32_native, uint32, 4);
#else
BV_INT_REF (u32, uint32, 4);
#endif
- VM_DEFINE_OP (113, bv_s32_ref, "bv-s32-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (121, bv_s32_ref, "bv-s32-ref", OP1 (X8_S8_S8_S8) | OP_DST)
#if SIZEOF_VOID_P > 4
BV_FIXABLE_INT_REF (s32, s32_native, int32, 4);
#else
BV_INT_REF (s32, int32, 4);
#endif
- VM_DEFINE_OP (114, bv_u64_ref, "bv-u64-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (122, bv_u64_ref, "bv-u64-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_INT_REF (u64, uint64, 8);
- VM_DEFINE_OP (115, bv_s64_ref, "bv-s64-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (123, bv_s64_ref, "bv-s64-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_INT_REF (s64, int64, 8);
- VM_DEFINE_OP (116, bv_f32_ref, "bv-f32-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (124, bv_f32_ref, "bv-f32-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FLOAT_REF (f32, ieee_single, float, 4);
- VM_DEFINE_OP (117, bv_f64_ref, "bv-f64-ref", OP1 (U8_U8_U8_U8) | OP_DST)
+ VM_DEFINE_OP (125, bv_f64_ref, "bv-f64-ref", OP1 (X8_S8_S8_S8) | OP_DST)
BV_FLOAT_REF (f64, ieee_double, double, 8);
/* bv-u8-set! dst:8 idx:8 src:8
@@ -3080,149 +3250,44 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
NEXT (1); \
} while (0)
- VM_DEFINE_OP (118, bv_u8_set, "bv-u8-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (126, bv_u8_set, "bv-u8-set!", OP1 (X8_S8_S8_S8))
BV_FIXABLE_INT_SET (u8, u8, uint8, 0, SCM_T_UINT8_MAX, 1);
- VM_DEFINE_OP (119, bv_s8_set, "bv-s8-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (127, bv_s8_set, "bv-s8-set!", OP1 (X8_S8_S8_S8))
BV_FIXABLE_INT_SET (s8, s8, int8, SCM_T_INT8_MIN, SCM_T_INT8_MAX, 1);
- VM_DEFINE_OP (120, bv_u16_set, "bv-u16-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (128, bv_u16_set, "bv-u16-set!", OP1 (X8_S8_S8_S8))
BV_FIXABLE_INT_SET (u16, u16_native, uint16, 0, SCM_T_UINT16_MAX, 2);
- VM_DEFINE_OP (121, bv_s16_set, "bv-s16-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (129, bv_s16_set, "bv-s16-set!", OP1 (X8_S8_S8_S8))
BV_FIXABLE_INT_SET (s16, s16_native, int16, SCM_T_INT16_MIN, SCM_T_INT16_MAX, 2);
- VM_DEFINE_OP (122, bv_u32_set, "bv-u32-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (130, bv_u32_set, "bv-u32-set!", OP1 (X8_S8_S8_S8))
#if SIZEOF_VOID_P > 4
BV_FIXABLE_INT_SET (u32, u32_native, uint32, 0, SCM_T_UINT32_MAX, 4);
#else
BV_INT_SET (u32, uint32, 4);
#endif
- VM_DEFINE_OP (123, bv_s32_set, "bv-s32-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (131, bv_s32_set, "bv-s32-set!", OP1 (X8_S8_S8_S8))
#if SIZEOF_VOID_P > 4
BV_FIXABLE_INT_SET (s32, s32_native, int32, SCM_T_INT32_MIN, SCM_T_INT32_MAX, 4);
#else
BV_INT_SET (s32, int32, 4);
#endif
- VM_DEFINE_OP (124, bv_u64_set, "bv-u64-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (132, bv_u64_set, "bv-u64-set!", OP1 (X8_S8_S8_S8))
BV_INT_SET (u64, uint64, 8);
- VM_DEFINE_OP (125, bv_s64_set, "bv-s64-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (133, bv_s64_set, "bv-s64-set!", OP1 (X8_S8_S8_S8))
BV_INT_SET (s64, int64, 8);
- VM_DEFINE_OP (126, bv_f32_set, "bv-f32-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (134, bv_f32_set, "bv-f32-set!", OP1 (X8_S8_S8_S8))
BV_FLOAT_SET (f32, ieee_single, float, 4);
- VM_DEFINE_OP (127, bv_f64_set, "bv-f64-set!", OP1 (U8_U8_U8_U8))
+ VM_DEFINE_OP (135, bv_f64_set, "bv-f64-set!", OP1 (X8_S8_S8_S8))
BV_FLOAT_SET (f64, ieee_double, double, 8);
- /* br-if-logtest a:12 b:12 invert:1 _:7 offset:24
- *
- * If the exact integer in A has any bits in common with the exact
- * integer in B, add OFFSET, a signed 24-bit number, to the current
- * instruction pointer.
- */
- VM_DEFINE_OP (128, br_if_logtest, "br-if-logtest", OP2 (U8_U12_U12, B1_X7_L24))
- {
- BR_BINARY (x, y,
- ((SCM_I_INUMP (x) && SCM_I_INUMP (y))
- ? (SCM_UNPACK (x) & SCM_UNPACK (y) & ~scm_tc2_int)
- : scm_is_true (scm_logtest (x, y))));
- }
-
- /* FIXME: Move above */
-
- /* allocate-struct dst:8 vtable:8 nfields:8
- *
- * Allocate a new struct with VTABLE, and place it in DST. The struct
- * will be constructed with space for NFIELDS fields, which should
- * correspond to the field count of the VTABLE.
- */
- VM_DEFINE_OP (129, allocate_struct, "allocate-struct", OP1 (U8_U8_U8_U8) | OP_DST)
- {
- scm_t_uint8 dst, vtable, nfields;
- SCM ret;
-
- UNPACK_8_8_8 (op, dst, vtable, nfields);
-
- SYNC_IP ();
- ret = scm_allocate_struct (LOCAL_REF (vtable), LOCAL_REF (nfields));
- LOCAL_SET (dst, ret);
-
- NEXT (1);
- }
-
- /* struct-ref dst:8 src:8 idx:8
- *
- * Fetch the item at slot IDX in the struct in SRC, and store it
- * in DST.
- */
- VM_DEFINE_OP (130, struct_ref, "struct-ref", OP1 (U8_U8_U8_U8) | OP_DST)
- {
- scm_t_uint8 dst, src, idx;
- SCM obj;
- SCM index;
-
- UNPACK_8_8_8 (op, dst, src, idx);
-
- obj = LOCAL_REF (src);
- index = LOCAL_REF (idx);
-
- if (SCM_LIKELY (SCM_STRUCTP (obj)
- && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
- SCM_VTABLE_FLAG_SIMPLE)
- && SCM_I_INUMP (index)
- && SCM_I_INUM (index) >= 0
- && SCM_I_INUM (index) < (SCM_STRUCT_DATA_REF
- (SCM_STRUCT_VTABLE (obj),
- scm_vtable_index_size))))
- RETURN (SCM_STRUCT_SLOT_REF (obj, SCM_I_INUM (index)));
-
- SYNC_IP ();
- RETURN (scm_struct_ref (obj, index));
- }
-
- /* struct-set! dst:8 idx:8 src:8
- *
- * Store SRC into the struct DST at slot IDX.
- */
- VM_DEFINE_OP (131, struct_set, "struct-set!", OP1 (U8_U8_U8_U8))
- {
- scm_t_uint8 dst, idx, src;
- SCM obj, val, index;
-
- UNPACK_8_8_8 (op, dst, idx, src);
-
- obj = LOCAL_REF (dst);
- val = LOCAL_REF (src);
- index = LOCAL_REF (idx);
-
- if (SCM_LIKELY (SCM_STRUCTP (obj)
- && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
- SCM_VTABLE_FLAG_SIMPLE)
- && SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
- SCM_VTABLE_FLAG_SIMPLE_RW)
- && SCM_I_INUMP (index)
- && SCM_I_INUM (index) >= 0
- && SCM_I_INUM (index) < (SCM_STRUCT_DATA_REF
- (SCM_STRUCT_VTABLE (obj),
- scm_vtable_index_size))))
- {
- SCM_STRUCT_SLOT_SET (obj, SCM_I_INUM (index), val);
- NEXT (1);
- }
-
- SYNC_IP ();
- scm_struct_set_x (obj, index, val);
- NEXT (1);
- }
-
- VM_DEFINE_OP (132, unused_132, NULL, NOP)
- VM_DEFINE_OP (133, unused_133, NULL, NOP)
- VM_DEFINE_OP (134, unused_134, NULL, NOP)
- VM_DEFINE_OP (135, unused_135, NULL, NOP)
VM_DEFINE_OP (136, unused_136, NULL, NOP)
VM_DEFINE_OP (137, unused_137, NULL, NOP)
VM_DEFINE_OP (138, unused_138, NULL, NOP)
diff --git a/module/language/bytecode.scm b/module/language/bytecode.scm
index 2ef98675a..089bf9e7e 100644
--- a/module/language/bytecode.scm
+++ b/module/language/bytecode.scm
@@ -34,34 +34,40 @@
(define (compute-instruction-arity name args)
(define (first-word-arity word)
(case word
- ((U8_X24) 0)
- ((U8_U24) 1)
- ((U8_L24) 1)
- ((U8_U8_I16) 2)
- ((U8_U12_U12) 2)
- ((U8_U8_U8_U8) 3)))
+ ((X32) 0)
+ ((X8_S24) 1)
+ ((X8_F24) 1)
+ ((X8_C24) 1)
+ ((X8_L24) 1)
+ ((X8_S8_I16) 2)
+ ((X8_S12_S12) 2)
+ ((X8_S12_C12) 2)
+ ((X8_C12_C12) 2)
+ ((X8_F12_F12) 2)
+ ((X8_S8_S8_S8) 3)
+ ((X8_S8_S8_C8) 3)
+ ((X8_S8_C8_S8) 3)))
(define (tail-word-arity word)
(case word
- ((U8_U24) 2)
- ((U8_L24) 2)
- ((U8_U8_I16) 3)
- ((U8_U12_U12) 3)
- ((U8_U8_U8_U8) 4)
- ((U32) 1)
+ ((C32) 1)
((I32) 1)
((A32) 1)
((B32) 0)
((N32) 1)
- ((S32) 1)
+ ((R32) 1)
((L32) 1)
((LO32) 1)
- ((X8_U24) 1)
- ((X8_U12_U12) 2)
- ((X8_L24) 1)
+ ((C8_C24) 2)
+ ((B1_C7_L24) 3)
+ ((B1_X7_S24) 2)
+ ((B1_X7_F24) 2)
+ ((B1_X7_C24) 2)
((B1_X7_L24) 2)
- ((B1_U7_L24) 3)
((B1_X31) 1)
- ((B1_X7_U24) 2)))
+ ((X8_S24) 1)
+ ((X8_F24) 1)
+ ((X8_C24) 1)
+ ((X8_L24) 1)))
(match args
((arg0 . args)
(fold (lambda (arg arity)
diff --git a/module/system/vm/assembler.scm b/module/system/vm/assembler.scm
index 6bc2bcf84..f29105108 100644
--- a/module/system/vm/assembler.scm
+++ b/module/system/vm/assembler.scm
@@ -89,13 +89,13 @@
emit-br-if-struct
emit-br-if-char
emit-br-if-tc7
- (emit-br-if-eq* . emit-br-if-eq)
- (emit-br-if-eqv* . emit-br-if-eqv)
- (emit-br-if-equal* . emit-br-if-equal)
- (emit-br-if-=* . emit-br-if-=)
- (emit-br-if-<* . emit-br-if-<)
- (emit-br-if-<=* . emit-br-if-<=)
- (emit-br-if-logtest* . emit-br-if-logtest)
+ emit-br-if-eq
+ emit-br-if-eqv
+ emit-br-if-equal
+ emit-br-if-=
+ emit-br-if-<
+ emit-br-if-<=
+ emit-br-if-logtest
(emit-mov* . emit-mov)
(emit-box* . emit-box)
(emit-box-ref* . emit-box-ref)
@@ -153,7 +153,7 @@
(emit-struct-ref* . emit-struct-ref)
(emit-struct-set!* . emit-struct-set!)
(emit-class-of* . emit-class-of)
- (emit-make-array* . emit-make-array)
+ emit-make-array
(emit-bv-u8-ref* . emit-bv-u8-ref)
(emit-bv-s8-ref* . emit-bv-s8-ref)
(emit-bv-u16-ref* . emit-bv-u16-ref)
@@ -510,29 +510,38 @@ later by the linker."
(with-syntax ((opcode opcode))
(op-case
asm type
- ((U8_X24)
+ ((X32)
(emit asm opcode))
- ((U8_U24 arg)
+ ((X8_S24 arg)
(emit asm (pack-u8-u24 opcode arg)))
- ((U8_L24 label)
+ ((X8_F24 arg)
+ (emit asm (pack-u8-u24 opcode arg)))
+ ((X8_C24 arg)
+ (emit asm (pack-u8-u24 opcode arg)))
+ ((X8_L24 label)
(record-label-reference asm label)
(emit asm opcode))
- ((U8_U8_I16 a imm)
+ ((X8_S8_I16 a imm)
(emit asm (pack-u8-u8-u16 opcode a (object-address imm))))
- ((U8_U12_U12 a b)
+ ((X8_S12_S12 a b)
(emit asm (pack-u8-u12-u12 opcode a b)))
- ((U8_U8_U8_U8 a b c)
+ ((X8_S12_C12 a b)
+ (emit asm (pack-u8-u12-u12 opcode a b)))
+ ((X8_C12_C12 a b)
+ (emit asm (pack-u8-u12-u12 opcode a b)))
+ ((X8_F12_F12 a b)
+ (emit asm (pack-u8-u12-u12 opcode a b)))
+ ((X8_S8_S8_S8 a b c)
+ (emit asm (pack-u8-u8-u8-u8 opcode a b c)))
+ ((X8_S8_S8_C8 a b c)
+ (emit asm (pack-u8-u8-u8-u8 opcode a b c)))
+ ((X8_S8_C8_S8 a b c)
(emit asm (pack-u8-u8-u8-u8 opcode a b c))))))
(define (pack-tail-word asm type)
(op-case
asm type
- ((U8_U24 a b)
- (emit asm (pack-u8-u24 a b)))
- ((U8_L24 a label)
- (record-label-reference asm label)
- (emit asm a))
- ((U32 a)
+ ((C32 a)
(emit asm a))
((I32 imm)
(let ((val (object-address imm)))
@@ -548,7 +557,7 @@ later by the linker."
((N32 label)
(record-far-label-reference asm label)
(emit asm 0))
- ((S32 label)
+ ((R32 label)
(record-far-label-reference asm label)
(emit asm 0))
((L32 label)
@@ -558,21 +567,31 @@ later by the linker."
(record-far-label-reference asm label
(* offset (/ (asm-word-size asm) 4)))
(emit asm 0))
- ((X8_U24 a)
- (emit asm (pack-u8-u24 0 a)))
- ((X8_L24 label)
- (record-label-reference asm label)
- (emit asm 0))
+ ((C8_C24 a b)
+ (emit asm (pack-u8-u24 a b)))
((B1_X7_L24 a label)
(record-label-reference asm label)
(emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
- ((B1_U7_L24 a b label)
+ ((B1_C7_L24 a b label)
(record-label-reference asm label)
(emit asm (pack-u1-u7-u24 (if a 1 0) b 0)))
((B1_X31 a)
(emit asm (pack-u1-u7-u24 (if a 1 0) 0 0)))
- ((B1_X7_U24 a b)
- (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))))
+ ((B1_X7_S24 a b)
+ (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
+ ((B1_X7_F24 a b)
+ (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
+ ((B1_X7_C24 a b)
+ (emit asm (pack-u1-u7-u24 (if a 1 0) 0 b)))
+ ((X8_S24 a)
+ (emit asm (pack-u8-u24 0 a)))
+ ((X8_F24 a)
+ (emit asm (pack-u8-u24 0 a)))
+ ((X8_C24 a)
+ (emit asm (pack-u8-u24 0 a)))
+ ((X8_L24 label)
+ (record-label-reference asm label)
+ (emit asm 0))))
(syntax-case x ()
((_ name opcode word0 word* ...)
@@ -651,25 +670,44 @@ later by the linker."
#f)))
(op-case
word0
- ((U8_U8_I16 ! a imm)
- (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
- imm))
- ((U8_U8_I16 <- a imm)
+ ((X8_S8_I16 <- a imm)
(values (if (< a (ash 1 8)) a 253)
imm))
- ((U8_U12_U12 ! a b)
+ ((X8_S12_S12 ! a b)
(values (if (< a (ash 1 12)) a (begin (emit-mov* asm 253 a) 253))
(if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
- ((U8_U12_U12 <- a b)
+ ((X8_S12_S12 <- a b)
(values (if (< a (ash 1 12)) a 253)
(if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
- ((U8_U8_U8_U8 ! a b c)
+ ((X8_S12_C12 <- a b)
+ (values (if (< a (ash 1 12)) a 253)
+ b))
+
+ ((X8_S8_S8_S8 ! a b c)
(values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
(if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
(if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
- ((U8_U8_U8_U8 <- a b c)
+ ((X8_S8_S8_S8 <- a b c)
(values (if (< a (ash 1 8)) a 253)
(if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
+ (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
+
+ ((X8_S8_S8_C8 ! a b c)
+ (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
+ (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
+ c))
+ ((X8_S8_S8_C8 <- a b c)
+ (values (if (< a (ash 1 8)) a 253)
+ (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
+ c))
+
+ ((X8_S8_C8_S8 ! a b c)
+ (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
+ b
+ (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
+ ((X8_S8_C8_S8 <- a b c)
+ (values (if (< a (ash 1 8)) a 253)
+ b
(if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))))
(define (tail-formals type)
@@ -682,22 +720,25 @@ later by the linker."
((op-case type)
(error "unmatched type" type))))
(op-case type
- (U8_U24 a b)
- (U8_L24 a label)
- (U32 a)
+ (C32 a)
(I32 imm)
(A32 imm)
(B32)
(N32 label)
- (S32 label)
+ (R32 label)
(L32 label)
(LO32 label offset)
- (X8_U24 a)
- (X8_L24 label)
+ (C8_C24 a b)
+ (B1_C7_L24 a b label)
+ (B1_X7_S24 a b)
+ (B1_X7_F24 a b)
+ (B1_X7_C24 a b)
(B1_X7_L24 a label)
- (B1_U7_L24 a b label)
(B1_X31 a)
- (B1_X7_U24 a b)))
+ (X8_S24 a)
+ (X8_F24 a)
+ (X8_C24 a)
+ (X8_L24 label)))
(define (shuffle-up dst)
(define-syntax op-case
@@ -711,10 +752,10 @@ later by the linker."
(with-syntax ((dst dst))
(op-case
word0
- ((U8_U8_I16 U8_U8_U8_U8)
+ ((X8_S8_I16 X8_S8_S8_S8 X8_S8_S8_C8 X8_S8_C8_S8)
(unless (< dst (ash 1 8))
(emit-mov* asm dst 253)))
- ((U8_U12_U12)
+ ((X8_S12_S12 X8_S12_C12)
(unless (< dst (ash 1 12))
(emit-mov* asm dst 253))))))
diff --git a/module/system/vm/disassembler.scm b/module/system/vm/disassembler.scm
index 08aa057a2..c1a8ce700 100644
--- a/module/system/vm/disassembler.scm
+++ b/module/system/vm/disassembler.scm
@@ -80,70 +80,58 @@
(define (parse-first-word word type)
(with-syntax ((word word))
(case type
- ((U8_X24)
+ ((X32)
#'())
- ((U8_U24)
+ ((X8_S24 X8_F24 X8_C24)
#'((ash word -8)))
- ((U8_L24)
+ ((X8_L24)
#'((unpack-s24 (ash word -8))))
- ((U8_U8_I16)
+ ((X8_S8_I16)
#'((logand (ash word -8) #xff)
(ash word -16)))
- ((U8_U12_U12)
+ ((X8_S12_S12
+ X8_S12_C12
+ X8_C12_C12
+ X8_F12_F12)
#'((logand (ash word -8) #xfff)
(ash word -20)))
- ((U8_U8_U8_U8)
+ ((X8_S8_S8_S8
+ X8_S8_S8_C8
+ X8_S8_C8_S8)
#'((logand (ash word -8) #xff)
(logand (ash word -16) #xff)
(ash word -24)))
(else
- (error "bad kind" type)))))
+ (error "bad head kind" type)))))
(define (parse-tail-word word type)
(with-syntax ((word word))
(case type
- ((U8_X24)
- #'((logand word #ff)))
- ((U8_U24)
+ ((C32 I32 A32 B32)
+ #'(word))
+ ((N32 R32 L32 LO32)
+ #'((unpack-s32 word)))
+ ((C8_C24)
#'((logand word #xff)
(ash word -8)))
- ((U8_L24)
- #'((logand word #xff)
- (unpack-s24 (ash word -8))))
- ((U32)
- #'(word))
- ((I32)
- #'(word))
- ((A32)
- #'(word))
- ((B32)
- #'(word))
- ((N32)
- #'((unpack-s32 word)))
- ((S32)
- #'((unpack-s32 word)))
- ((L32)
- #'((unpack-s32 word)))
- ((LO32)
- #'((unpack-s32 word)))
- ((X8_U24)
- #'((ash word -8)))
- ((X8_L24)
- #'((unpack-s24 (ash word -8))))
- ((B1_X7_L24)
- #'((not (zero? (logand word #x1)))
- (unpack-s24 (ash word -8))))
- ((B1_U7_L24)
+ ((B1_C7_L24)
#'((not (zero? (logand word #x1)))
(logand (ash word -1) #x7f)
(unpack-s24 (ash word -8))))
- ((B1_X31)
- #'((not (zero? (logand word #x1)))))
- ((B1_X7_U24)
+ ((B1_X7_S24 B1_X7_F24 B1_X7_C24)
#'((not (zero? (logand word #x1)))
(ash word -8)))
+ ((B1_X7_L24)
+ #'((not (zero? (logand word #x1)))
+ (unpack-s24 (ash word -8))))
+ ((B1_X31)
+ #'((not (zero? (logand word #x1)))))
+ ((X8_S24 X8_F24 X8_C24)
+ #'((ash word -8)))
+ ((X8_L24)
+ #'((unpack-s24 (ash word -8))))
(else
- (error "bad kind" type)))))
+ (error "bad tail kind" type)))))
(syntax-case x ()
((_ name opcode word0 word* ...)
From 9b1ac02a8584ee7ca73a8f5920d7b33c0487bfc0 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 21 Oct 2015 10:48:15 +0200
Subject: [PATCH 059/865] Fix boot closure wrong-num-args error
* libguile/eval.c (prepare_boot_closure_env_for_eval): Fix issue if
fixed closure is called with wrong number of arguments during
bootstrap.
---
libguile/eval.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libguile/eval.c b/libguile/eval.c
index 09fa71df4..6f2751970 100644
--- a/libguile/eval.c
+++ b/libguile/eval.c
@@ -882,7 +882,8 @@ prepare_boot_closure_env_for_eval (SCM proc, unsigned int argc,
*out_body = BOOT_CLOSURE_BODY (proc);
*inout_env = new_env;
}
- else if (BOOT_CLOSURE_IS_REST (proc) && argc >= nreq)
+ else if (!BOOT_CLOSURE_IS_FIXED (proc) &&
+ BOOT_CLOSURE_IS_REST (proc) && argc >= nreq)
{
SCM rest;
int i;
From 70c317ab5173e26d9f2a9b8b81a9441ef3ef7008 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 21 Oct 2015 10:48:58 +0200
Subject: [PATCH 060/865] SP-relative local addressing
* libguile/vm-engine.c: S24/S12/S8 operands addressed relative to the
SP, not the FP. Cache the SP instead of a FP-relative locals
pointer. Further cleanups to follow.
* libguile/vm.c (vm_builtin_call_with_values_code): Adapt to mov operand
addresing change.
* module/language/cps/compile-bytecode.scm (compile-function): Reify
SP-relative local indexes where appropriate.
* module/system/vm/assembler.scm (emit-fmov*): New helper, exported as
emit-fmov.
(shuffling-assembler, define-shuffling-assembler): Rewrite to shuffle
via push/pop/drop.
(standard-prelude, opt-prelude, kw-prelude): No need to provide for
shuffling args.
* test-suite/tests/rtl.test: Update.
* module/language/cps/slot-allocation.scm: Don't reserve slots 253-255.
---
libguile/vm-engine.c | 461 +++++++++++------------
libguile/vm.c | 4 +-
module/language/cps/compile-bytecode.scm | 185 +++++----
module/language/cps/slot-allocation.scm | 23 +-
module/system/vm/assembler.scm | 324 ++++++++--------
test-suite/tests/rtl.test | 114 +++---
6 files changed, 562 insertions(+), 549 deletions(-)
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index df7a528eb..ca369bd99 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -107,7 +107,7 @@
{ \
SYNC_IP (); \
exp; \
- CACHE_LOCALS (); \
+ CACHE_SP (); \
} \
} while (0)
#else
@@ -128,7 +128,7 @@
RUN_HOOK0 (abort)
#define VM_HANDLE_INTERRUPTS \
- SCM_ASYNC_TICK_WITH_GUARD_CODE (thread, SYNC_IP (), CACHE_LOCALS ())
+ SCM_ASYNC_TICK_WITH_GUARD_CODE (thread, SYNC_IP (), CACHE_SP ())
/* Virtual Machine
@@ -136,31 +136,29 @@
The VM has three state bits: the instruction pointer (IP), the frame
pointer (FP), and the stack pointer (SP). We cache the IP in a
machine register, local to the VM, because it is used extensively by
- the VM. We cache the address of local 0 too, for now; when we change
- to reference variables relative to the SP we'll cache the SP instead.
- As it is, the SP is used more by code outside the VM than by the VM
- itself, we don't bother caching it locally.
+ the VM. We do the same for SP. The FP is used more by code outside
+ the VM than by the VM itself, we don't bother caching it locally.
Keeping vp->ip in sync with the local IP would be a big lose, as it
is updated so often. Instead of updating vp->ip all the time, we
call SYNC_IP whenever we would need to know the IP of the top frame.
In practice, we need to SYNC_IP whenever we call out of the VM to a
function that would like to walk the stack, perhaps as the result of
- an exception.
+ an exception. On the other hand, we do always keep vp->sp in sync
+ with the local SP.
One more thing. We allow the stack to move, when it expands.
Therefore if you call out to a C procedure that could call Scheme
code, or otherwise push anything on the stack, you will need to
- CACHE_LOCALS afterwards to restore the possibly-changed address of
- local 0. */
+ CACHE_SP afterwards to restore the possibly-changed stack pointer. */
#define SYNC_IP() vp->ip = (ip)
-#define CACHE_LOCALS() locals = (vp->fp - 1)
+#define CACHE_SP() sp = vp->sp
#define CACHE_REGISTER() \
do { \
ip = vp->ip; \
- CACHE_LOCALS (); \
+ CACHE_SP (); \
} while (0)
@@ -174,38 +172,36 @@
FP is valid across an ALLOC_FRAME call. Be careful! */
#define ALLOC_FRAME(n) \
do { \
- union scm_vm_stack_element *new_sp = LOCAL_ADDRESS (n - 1); \
- if (new_sp < vp->sp_min_since_gc) \
+ sp = vp->fp - (n); \
+ if (sp < vp->sp_min_since_gc) \
{ \
- if (SCM_UNLIKELY (new_sp < vp->stack_limit)) \
+ if (SCM_UNLIKELY (sp < vp->stack_limit)) \
{ \
SYNC_IP (); \
- vm_expand_stack (vp, new_sp); \
- CACHE_LOCALS (); \
+ vm_expand_stack (vp, sp); \
+ CACHE_SP (); \
} \
else \
- vp->sp_min_since_gc = vp->sp = new_sp; \
+ vp->sp_min_since_gc = vp->sp = sp; \
} \
else \
- vp->sp = new_sp; \
+ vp->sp = sp; \
} while (0)
/* Reset the current frame to hold N locals. Used when we know that no
stack expansion is needed. */
#define RESET_FRAME(n) \
do { \
- vp->sp = LOCAL_ADDRESS (n - 1); \
- if (vp->sp < vp->sp_min_since_gc) \
- vp->sp_min_since_gc = vp->sp; \
+ vp->sp = sp = vp->fp - (n); \
+ if (sp < vp->sp_min_since_gc) \
+ vp->sp_min_since_gc = sp; \
} while (0)
/* Compute the number of locals in the frame. At a call, this is equal
to the number of actual arguments when a function is first called,
plus one for the function. */
-#define FRAME_LOCALS_COUNT_FROM(slot) \
- (LOCAL_ADDRESS (slot) + 1 - vp->sp)
-#define FRAME_LOCALS_COUNT() \
- FRAME_LOCALS_COUNT_FROM (0)
+#define FRAME_LOCALS_COUNT() (vp->fp - sp)
+#define FRAME_LOCALS_COUNT_FROM(slot) (FRAME_LOCALS_COUNT () - slot)
/* Restore registers after returning from a frame. */
#define RESTORE_FRAME() \
@@ -248,18 +244,12 @@
case opcode:
#endif
-// This "locals + 1" is actually an optimization, because vp->fp points
-// on before the zeroeth local. The result is to reference locals[-i].
-// In the future we should change to reference locals relative to the SP
-// and cache the SP instead, which would give direct (non-negated)
-// indexing off the SP, which is more in line with addressing modes
-// supported by common CPUs.
-#define LOCAL_ADDRESS(i) SCM_FRAME_SLOT (locals + 1, i)
-#define LOCAL_REF(i) SCM_FRAME_LOCAL (locals + 1, i)
-#define LOCAL_SET(i,o) SCM_FRAME_LOCAL (locals + 1, i) = o
+#define FP_SLOT(i) SCM_FRAME_SLOT (vp->fp, i)
+#define FP_REF(i) SCM_FRAME_LOCAL (vp->fp, i)
+#define FP_SET(i,o) SCM_FRAME_LOCAL (vp->fp, i) = o
-#define SP_REF(i) (vp->sp[i].as_scm)
-#define SP_SET(i,o) (vp->sp[i].as_scm = o)
+#define SP_REF(i) (sp[i].as_scm)
+#define SP_SET(i,o) (sp[i].as_scm = o)
#define VARIABLE_REF(v) SCM_VARIABLE_REF (v)
#define VARIABLE_SET(v,o) SCM_VARIABLE_SET (v, o)
@@ -272,15 +262,13 @@
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (2); \
old_fp = vp->fp; \
- ip = SCM_FRAME_RETURN_ADDRESS (vp->fp); \
- vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp); \
- CACHE_LOCALS (); \
+ ip = SCM_FRAME_RETURN_ADDRESS (old_fp); \
+ vp->fp = SCM_FRAME_DYNAMIC_LINK (old_fp); \
/* Clear frame. */ \
old_fp[0].as_scm = SCM_BOOL_F; \
old_fp[1].as_scm = SCM_BOOL_F; \
/* Leave proc. */ \
- SCM_FRAME_LOCAL (old_fp, 1) = val; \
- vp->sp = SCM_FRAME_SLOT (old_fp, 1); \
+ SP_SET (0, val); \
POP_CONTINUATION_HOOK (old_fp); \
NEXT (0); \
} while (0)
@@ -292,9 +280,9 @@
SCM vals = vals_; \
VM_HANDLE_INTERRUPTS; \
ALLOC_FRAME (3); \
- SCM_FRAME_LOCAL (vp->fp, 0) = vm_builtin_apply; \
- SCM_FRAME_LOCAL (vp->fp, 1) = vm_builtin_values; \
- SCM_FRAME_LOCAL (vp->fp, 2) = vals; \
+ SP_SET (2, vm_builtin_apply); \
+ SP_SET (1, vm_builtin_values); \
+ SP_SET (0, vals); \
ip = (scm_t_uint32 *) vm_builtin_apply_code; \
goto op_tail_apply; \
} while (0)
@@ -314,7 +302,7 @@
scm_t_uint32 test; \
SCM x; \
UNPACK_24 (op, test); \
- x = LOCAL_REF (test); \
+ x = SP_REF (test); \
if ((ip[1] & 0x1) ? !(exp) : (exp)) \
{ \
scm_t_int32 offset = ip[1]; \
@@ -330,8 +318,8 @@
SCM x, y; \
UNPACK_24 (op, a); \
UNPACK_24 (ip[1], b); \
- x = LOCAL_REF (a); \
- y = LOCAL_REF (b); \
+ x = SP_REF (a); \
+ y = SP_REF (b); \
if ((ip[2] & 0x1) ? !(exp) : (exp)) \
{ \
scm_t_int32 offset = ip[2]; \
@@ -348,8 +336,8 @@
SCM x, y; \
UNPACK_24 (op, a); \
UNPACK_24 (ip[1], b); \
- x = LOCAL_REF (a); \
- y = LOCAL_REF (b); \
+ x = SP_REF (a); \
+ y = SP_REF (b); \
if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) \
{ \
scm_t_signed_bits x_bits = SCM_UNPACK (x); \
@@ -369,7 +357,7 @@
SCM res; \
SYNC_IP (); \
res = srel (x, y); \
- CACHE_LOCALS (); \
+ CACHE_SP (); \
if ((ip[2] & 0x1) ? scm_is_false (res) : scm_is_true (res)) \
{ \
scm_t_int32 offset = ip[2]; \
@@ -386,17 +374,17 @@
scm_t_uint16 dst, src; \
SCM a1; \
UNPACK_12_12 (op, dst, src); \
- a1 = LOCAL_REF (src)
+ a1 = SP_REF (src)
#define ARGS2(a1, a2) \
scm_t_uint8 dst, src1, src2; \
SCM a1, a2; \
UNPACK_8_8_8 (op, dst, src1, src2); \
- a1 = LOCAL_REF (src1); \
- a2 = LOCAL_REF (src2)
+ a1 = SP_REF (src1); \
+ a2 = SP_REF (src2)
#define RETURN(x) \
- do { LOCAL_SET (dst, x); NEXT (1); } while (0)
+ do { SP_SET (dst, x); NEXT (1); } while (0)
#define RETURN_EXP(exp) \
- do { SCM __x; SYNC_IP (); __x = exp; CACHE_LOCALS (); RETURN (__x); } while (0)
+ do { SCM __x; SYNC_IP (); __x = exp; CACHE_SP (); RETURN (__x); } while (0)
/* The maximum/minimum tagged integers. */
#define INUM_MAX \
@@ -440,10 +428,10 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
running. */
register scm_t_uint32 *ip IP_REG;
- /* Frame pointer: A pointer into the stack, off of which we index
- arguments and local variables. Pushed at function calls, popped on
- returns. */
- register union scm_vm_stack_element *locals FP_REG;
+ /* Stack pointer: A pointer to the hot end of the stack, off of which
+ we index arguments and local variables. Pushed at function calls,
+ popped on returns. */
+ register union scm_vm_stack_element *sp FP_REG;
/* Current opcode: A cache of *ip. */
register scm_t_uint32 op;
@@ -473,13 +461,13 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
NEXT (0);
apply:
- while (!SCM_PROGRAM_P (LOCAL_REF (0)))
+ while (!SCM_PROGRAM_P (FP_REF (0)))
{
- SCM proc = LOCAL_REF (0);
+ SCM proc = FP_REF (0);
if (SCM_STRUCTP (proc) && SCM_STRUCT_APPLICABLE_P (proc))
{
- LOCAL_SET (0, SCM_STRUCT_PROCEDURE (proc));
+ FP_SET (0, SCM_STRUCT_PROCEDURE (proc));
continue;
}
if (SCM_HAS_TYP7 (proc, scm_tc7_smob) && SCM_SMOB_APPLICABLE_P (proc))
@@ -490,9 +478,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
IP and go. ) */
ALLOC_FRAME (n + 1);
while (n--)
- LOCAL_SET (n + 1, LOCAL_REF (n));
+ FP_SET (n + 1, FP_REF (n));
- LOCAL_SET (0, SCM_SMOB_DESCRIPTOR (proc).apply_trampoline);
+ FP_SET (0, SCM_SMOB_DESCRIPTOR (proc).apply_trampoline);
continue;
}
@@ -501,7 +489,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
}
/* Let's go! */
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (FP_REF (0));
APPLY_HOOK ();
@@ -528,13 +516,13 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SCM ret;
if (nvals == 1)
- ret = LOCAL_REF (4);
+ ret = FP_REF (4);
else
{
scm_t_uint32 n;
ret = SCM_EOL;
for (n = nvals; n > 0; n--)
- ret = scm_inline_cons (thread, LOCAL_REF (4 + n - 1), ret);
+ ret = scm_inline_cons (thread, FP_REF (4 + n - 1), ret);
ret = scm_values (ret);
}
@@ -572,16 +560,15 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
old_fp = vp->fp;
vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
- CACHE_LOCALS ();
SCM_FRAME_SET_DYNAMIC_LINK (vp->fp, old_fp);
SCM_FRAME_SET_RETURN_ADDRESS (vp->fp, ip + 2);
RESET_FRAME (nlocals);
- if (SCM_UNLIKELY (!SCM_PROGRAM_P (LOCAL_REF (0))))
+ if (SCM_UNLIKELY (!SCM_PROGRAM_P (FP_REF (0))))
goto apply;
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (FP_REF (0));
APPLY_HOOK ();
@@ -614,7 +601,6 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
old_fp = vp->fp;
vp->fp = SCM_FRAME_SLOT (old_fp, proc - 1);
- CACHE_LOCALS ();
SCM_FRAME_SET_DYNAMIC_LINK (vp->fp, old_fp);
SCM_FRAME_SET_RETURN_ADDRESS (vp->fp, ip + 3);
@@ -643,10 +629,10 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
RESET_FRAME (nlocals);
- if (SCM_UNLIKELY (!SCM_PROGRAM_P (LOCAL_REF (0))))
+ if (SCM_UNLIKELY (!SCM_PROGRAM_P (FP_REF (0))))
goto apply;
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (FP_REF (0));
APPLY_HOOK ();
@@ -696,14 +682,14 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
nlocals = FRAME_LOCALS_COUNT ();
for (n = 0; from + n < nlocals; n++)
- LOCAL_SET (n + 1, LOCAL_REF (from + n));
+ FP_SET (n + 1, FP_REF (from + n));
RESET_FRAME (n + 1);
- if (SCM_UNLIKELY (!SCM_PROGRAM_P (LOCAL_REF (0))))
+ if (SCM_UNLIKELY (!SCM_PROGRAM_P (FP_REF (0))))
goto apply;
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (FP_REF (0));
APPLY_HOOK ();
@@ -723,7 +709,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, dst, proc);
UNPACK_24 (ip[1], nlocals);
VM_ASSERT (FRAME_LOCALS_COUNT () > proc + 1, vm_error_no_values ());
- LOCAL_SET (dst, LOCAL_REF (proc + 1));
+ FP_SET (dst, FP_REF (proc + 1));
RESET_FRAME (nlocals);
NEXT (2);
}
@@ -758,7 +744,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint32 src;
UNPACK_24 (op, src);
- RETURN_ONE_VALUE (LOCAL_REF (src));
+ RETURN_ONE_VALUE (SP_REF (src));
}
/* return-values _:24
@@ -778,7 +764,6 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
old_fp = vp->fp;
ip = SCM_FRAME_RETURN_ADDRESS (vp->fp);
vp->fp = SCM_FRAME_DYNAMIC_LINK (vp->fp);
- CACHE_LOCALS ();
/* Clear stack frame. */
old_fp[0].as_scm = SCM_BOOL_F;
@@ -811,63 +796,64 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, ptr_idx);
- pointer = SCM_PROGRAM_FREE_VARIABLE_REF (LOCAL_REF (0), ptr_idx);
+ pointer = SCM_PROGRAM_FREE_VARIABLE_REF (FP_REF (0), ptr_idx);
subr = SCM_POINTER_VALUE (pointer);
SYNC_IP ();
+ // FIXME!!!!
switch (FRAME_LOCALS_COUNT_FROM (1))
{
case 0:
ret = subr ();
break;
case 1:
- ret = subr (LOCAL_REF (1));
+ ret = subr (FP_REF (1));
break;
case 2:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2));
+ ret = subr (FP_REF (1), FP_REF (2));
break;
case 3:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3));
break;
case 4:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4));
break;
case 5:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5));
break;
case 6:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5), FP_REF (6));
break;
case 7:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
- LOCAL_REF (7));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5), FP_REF (6),
+ FP_REF (7));
break;
case 8:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
- LOCAL_REF (7), LOCAL_REF (8));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5), FP_REF (6),
+ FP_REF (7), FP_REF (8));
break;
case 9:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
- LOCAL_REF (7), LOCAL_REF (8), LOCAL_REF (9));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5), FP_REF (6),
+ FP_REF (7), FP_REF (8), FP_REF (9));
break;
case 10:
- ret = subr (LOCAL_REF (1), LOCAL_REF (2), LOCAL_REF (3),
- LOCAL_REF (4), LOCAL_REF (5), LOCAL_REF (6),
- LOCAL_REF (7), LOCAL_REF (8), LOCAL_REF (9),
- LOCAL_REF (10));
+ ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
+ FP_REF (4), FP_REF (5), FP_REF (6),
+ FP_REF (7), FP_REF (8), FP_REF (9),
+ FP_REF (10));
break;
default:
abort ();
}
- CACHE_LOCALS ();
+ CACHE_SP ();
if (SCM_UNLIKELY (SCM_VALUESP (ret)))
/* multiple values returned to continuation */
@@ -891,17 +877,16 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, cif_idx, ptr_idx);
- closure = LOCAL_REF (0);
+ closure = FP_REF (0);
cif = SCM_PROGRAM_FREE_VARIABLE_REF (closure, cif_idx);
pointer = SCM_PROGRAM_FREE_VARIABLE_REF (closure, ptr_idx);
SYNC_IP ();
// FIXME: separate args
- ret = scm_i_foreign_call (scm_inline_cons (thread, cif, pointer),
- vp->sp);
+ ret = scm_i_foreign_call (scm_inline_cons (thread, cif, pointer), sp);
- CACHE_LOCALS ();
+ CACHE_SP ();
if (SCM_UNLIKELY (SCM_VALUESP (ret)))
/* multiple values returned to continuation */
@@ -926,14 +911,14 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, contregs_idx);
contregs =
- SCM_PROGRAM_FREE_VARIABLE_REF (LOCAL_REF (0), contregs_idx);
+ SCM_PROGRAM_FREE_VARIABLE_REF (FP_REF (0), contregs_idx);
SYNC_IP ();
scm_i_check_continuation (contregs);
vm_return_to_continuation (scm_i_contregs_vp (contregs),
scm_i_contregs_vm_cont (contregs),
FRAME_LOCALS_COUNT_FROM (1),
- vp->sp);
+ sp);
scm_i_reinstate_continuation (contregs);
/* no NEXT */
@@ -954,7 +939,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint32 cont_idx;
UNPACK_24 (op, cont_idx);
- vmcont = SCM_PROGRAM_FREE_VARIABLE_REF (LOCAL_REF (0), cont_idx);
+ vmcont = SCM_PROGRAM_FREE_VARIABLE_REF (FP_REF (0), cont_idx);
SYNC_IP ();
VM_ASSERT (SCM_VM_CONT_REWINDABLE_P (vmcont),
@@ -982,7 +967,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
// At a minimum, there should be apply, f, and the list.
VM_ASSERT (nlocals >= 3, abort ());
list_idx = nlocals - 1;
- list = LOCAL_REF (list_idx);
+ list = FP_REF (list_idx);
list_len = scm_ilength (list);
VM_ASSERT (list_len >= 0, vm_error_apply_to_non_list (list));
@@ -991,20 +976,20 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ALLOC_FRAME (nlocals);
for (i = 1; i < list_idx; i++)
- LOCAL_SET (i - 1, LOCAL_REF (i));
+ FP_SET (i - 1, FP_REF (i));
/* Null out these slots, just in case there are less than 2 elements
in the list. */
- LOCAL_SET (list_idx - 1, SCM_UNDEFINED);
- LOCAL_SET (list_idx, SCM_UNDEFINED);
+ FP_SET (list_idx - 1, SCM_UNDEFINED);
+ FP_SET (list_idx, SCM_UNDEFINED);
for (i = 0; i < list_len; i++, list = SCM_CDR (list))
- LOCAL_SET (list_idx - 1 + i, SCM_CAR (list));
+ FP_SET (list_idx - 1 + i, SCM_CAR (list));
- if (SCM_UNLIKELY (!SCM_PROGRAM_P (LOCAL_REF (0))))
+ if (SCM_UNLIKELY (!SCM_PROGRAM_P (FP_REF (0))))
goto apply;
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (FP_REF (0));
APPLY_HOOK ();
@@ -1042,14 +1027,15 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
if (first)
{
- LOCAL_SET (0, LOCAL_REF (1));
- LOCAL_SET (1, cont);
RESET_FRAME (2);
- if (SCM_UNLIKELY (!SCM_PROGRAM_P (LOCAL_REF (0))))
+ SP_SET (1, SP_REF (0));
+ SP_SET (0, cont);
+
+ if (SCM_UNLIKELY (!SCM_PROGRAM_P (SP_REF (1))))
goto apply;
- ip = SCM_PROGRAM_CODE (LOCAL_REF (0));
+ ip = SCM_PROGRAM_CODE (SP_REF (1));
APPLY_HOOK ();
@@ -1079,7 +1065,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
it continues with the next instruction. */
ip++;
SYNC_IP ();
- vm_abort (vp, LOCAL_REF (1), nlocals - 2, registers);
+ vm_abort (vp, FP_REF (1), nlocals - 2, registers);
/* vm_abort should not return */
abort ();
@@ -1094,7 +1080,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 dst, idx;
UNPACK_12_12 (op, dst, idx);
- LOCAL_SET (dst, scm_vm_builtin_ref (idx));
+ SP_SET (dst, scm_vm_builtin_ref (idx));
NEXT (1);
}
@@ -1139,7 +1125,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint32 expected;
UNPACK_24 (op, expected);
VM_ASSERT (FRAME_LOCALS_COUNT () == expected,
- vm_error_wrong_num_args (LOCAL_REF (0)));
+ vm_error_wrong_num_args (FP_REF (0)));
NEXT (1);
}
VM_DEFINE_OP (22, assert_nargs_ge, "assert-nargs-ge", OP1 (X8_C24))
@@ -1147,7 +1133,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint32 expected;
UNPACK_24 (op, expected);
VM_ASSERT (FRAME_LOCALS_COUNT () >= expected,
- vm_error_wrong_num_args (LOCAL_REF (0)));
+ vm_error_wrong_num_args (FP_REF (0)));
NEXT (1);
}
VM_DEFINE_OP (23, assert_nargs_le, "assert-nargs-le", OP1 (X8_C24))
@@ -1155,7 +1141,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint32 expected;
UNPACK_24 (op, expected);
VM_ASSERT (FRAME_LOCALS_COUNT () <= expected,
- vm_error_wrong_num_args (LOCAL_REF (0)));
+ vm_error_wrong_num_args (FP_REF (0)));
NEXT (1);
}
@@ -1173,7 +1159,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
nargs = FRAME_LOCALS_COUNT ();
ALLOC_FRAME (nlocals);
while (nlocals-- > nargs)
- LOCAL_SET (nlocals, SCM_UNDEFINED);
+ FP_SET (nlocals, SCM_UNDEFINED);
NEXT (1);
}
@@ -1219,7 +1205,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, dst);
val = SP_REF (0);
- vp->sp++;
+ vp->sp = sp = sp + 1;
SP_SET (dst, val);
NEXT (1);
}
@@ -1233,7 +1219,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint32 count;
UNPACK_24 (op, count);
- vp->sp += count;
+ vp->sp = sp = sp + count;
NEXT (1);
}
@@ -1247,10 +1233,10 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 expected, nlocals;
UNPACK_12_12 (op, expected, nlocals);
VM_ASSERT (FRAME_LOCALS_COUNT () == expected,
- vm_error_wrong_num_args (LOCAL_REF (0)));
+ vm_error_wrong_num_args (FP_REF (0)));
ALLOC_FRAME (expected + nlocals);
while (nlocals--)
- LOCAL_SET (expected + nlocals, SCM_UNDEFINED);
+ SP_SET (nlocals, SCM_UNDEFINED);
NEXT (1);
}
@@ -1278,9 +1264,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint32 n;
for (n = nreq; n < npos; n++)
- if (scm_is_keyword (LOCAL_REF (n)))
+ if (scm_is_keyword (FP_REF (n)))
break;
- if (n == npos && !scm_is_keyword (LOCAL_REF (n)))
+ if (n == npos && !scm_is_keyword (FP_REF (n)))
{
scm_t_int32 offset = ip[2];
offset >>= 8; /* Sign-extending shift. */
@@ -1331,7 +1317,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
/* and we still have positionals to fill */
&& npositional < nreq_and_opt
/* and we haven't reached a keyword yet */
- && !scm_is_keyword (LOCAL_REF (npositional)))
+ && !scm_is_keyword (FP_REF (npositional)))
/* bind this optional arg (by leaving it in place) */
npositional++;
nkw = nargs - npositional;
@@ -1339,44 +1325,44 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ALLOC_FRAME (ntotal + nkw);
n = nkw;
while (n--)
- LOCAL_SET (ntotal + n, LOCAL_REF (npositional + n));
+ FP_SET (ntotal + n, FP_REF (npositional + n));
/* and fill optionals & keyword args with SCM_UNDEFINED */
n = npositional;
while (n < ntotal)
- LOCAL_SET (n++, SCM_UNDEFINED);
+ FP_SET (n++, SCM_UNDEFINED);
VM_ASSERT (has_rest || (nkw % 2) == 0,
- vm_error_kwargs_length_not_even (LOCAL_REF (0)));
+ vm_error_kwargs_length_not_even (FP_REF (0)));
/* Now bind keywords, in the order given. */
for (n = 0; n < nkw; n++)
- if (scm_is_keyword (LOCAL_REF (ntotal + n)))
+ if (scm_is_keyword (FP_REF (ntotal + n)))
{
SCM walk;
for (walk = kw; scm_is_pair (walk); walk = SCM_CDR (walk))
- if (scm_is_eq (SCM_CAAR (walk), LOCAL_REF (ntotal + n)))
+ if (scm_is_eq (SCM_CAAR (walk), FP_REF (ntotal + n)))
{
SCM si = SCM_CDAR (walk);
- LOCAL_SET (SCM_I_INUMP (si) ? SCM_I_INUM (si) : scm_to_uint32 (si),
- LOCAL_REF (ntotal + n + 1));
+ FP_SET (SCM_I_INUMP (si) ? SCM_I_INUM (si) : scm_to_uint32 (si),
+ FP_REF (ntotal + n + 1));
break;
}
VM_ASSERT (scm_is_pair (walk) || allow_other_keys,
- vm_error_kwargs_unrecognized_keyword (LOCAL_REF (0),
- LOCAL_REF (ntotal + n)));
+ vm_error_kwargs_unrecognized_keyword (FP_REF (0),
+ FP_REF (ntotal + n)));
n++;
}
else
- VM_ASSERT (has_rest, vm_error_kwargs_invalid_keyword (LOCAL_REF (0),
- LOCAL_REF (ntotal + n)));
+ VM_ASSERT (has_rest, vm_error_kwargs_invalid_keyword (FP_REF (0),
+ FP_REF (ntotal + n)));
if (has_rest)
{
SCM rest = SCM_EOL;
n = nkw;
while (n--)
- rest = scm_inline_cons (thread, LOCAL_REF (ntotal + n), rest);
- LOCAL_SET (nreq_and_opt, rest);
+ rest = scm_inline_cons (thread, FP_REF (ntotal + n), rest);
+ FP_SET (nreq_and_opt, rest);
}
RESET_FRAME (ntotal);
@@ -1401,20 +1387,20 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
ALLOC_FRAME (dst + 1);
while (nargs < dst)
- LOCAL_SET (nargs++, SCM_UNDEFINED);
+ FP_SET (nargs++, SCM_UNDEFINED);
}
else
{
while (nargs-- > dst)
{
- rest = scm_inline_cons (thread, LOCAL_REF (nargs), rest);
- LOCAL_SET (nargs, SCM_UNDEFINED);
+ rest = scm_inline_cons (thread, FP_REF (nargs), rest);
+ FP_SET (nargs, SCM_UNDEFINED);
}
RESET_FRAME (dst + 1);
}
- LOCAL_SET (dst, rest);
+ FP_SET (dst, rest);
NEXT (1);
}
@@ -1539,7 +1525,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* If the value in A is equal? to the value in B, add OFFSET, a signed
* 24-bit number, to the current instruction pointer.
*/
- // FIXME: Should sync_ip before calling out and cache_locals before coming
+ // FIXME: Should sync_ip before calling out and cache_sp before coming
// back! Another reason to remove this opcode!
VM_DEFINE_OP (43, br_if_equal, "br-if-equal", OP3 (X8_S24, X8_S24, B1_X7_L24))
{
@@ -1610,7 +1596,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 src;
UNPACK_12_12 (op, dst, src);
- LOCAL_SET (dst, LOCAL_REF (src));
+ SP_SET (dst, SP_REF (src));
NEXT (1);
}
@@ -1626,7 +1612,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, dst);
UNPACK_24 (ip[1], src);
- LOCAL_SET (dst, LOCAL_REF (src));
+ SP_SET (dst, SP_REF (src));
NEXT (2);
}
@@ -1643,7 +1629,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, dst);
UNPACK_24 (ip[1], src);
- LOCAL_SET (dst, LOCAL_REF (src));
+ FP_SET (dst, FP_REF (src));
NEXT (2);
}
@@ -1656,8 +1642,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
{
scm_t_uint16 dst, src;
UNPACK_12_12 (op, dst, src);
- LOCAL_SET (dst, scm_inline_cell (thread, scm_tc7_variable,
- SCM_UNPACK (LOCAL_REF (src))));
+ SP_SET (dst, scm_inline_cell (thread, scm_tc7_variable,
+ SCM_UNPACK (SP_REF (src))));
NEXT (1);
}
@@ -1671,11 +1657,11 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 dst, src;
SCM var;
UNPACK_12_12 (op, dst, src);
- var = LOCAL_REF (src);
+ var = SP_REF (src);
VM_ASSERT (SCM_VARIABLEP (var),
vm_error_not_a_variable ("variable-ref", var));
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (var));
- LOCAL_SET (dst, VARIABLE_REF (var));
+ SP_SET (dst, VARIABLE_REF (var));
NEXT (1);
}
@@ -1688,10 +1674,10 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 dst, src;
SCM var;
UNPACK_12_12 (op, dst, src);
- var = LOCAL_REF (dst);
+ var = SP_REF (dst);
VM_ASSERT (SCM_VARIABLEP (var),
vm_error_not_a_variable ("variable-set!", var));
- VARIABLE_SET (var, LOCAL_REF (src));
+ VARIABLE_SET (var, SP_REF (src));
NEXT (1);
}
@@ -1719,7 +1705,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
// FIXME: Elide these initializations?
for (n = 0; n < nfree; n++)
SCM_PROGRAM_FREE_VARIABLE_SET (closure, n, SCM_BOOL_F);
- LOCAL_SET (dst, closure);
+ SP_SET (dst, closure);
NEXT (3);
}
@@ -1734,7 +1720,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, dst, src);
UNPACK_24 (ip[1], idx);
/* CHECK_FREE_VARIABLE (src); */
- LOCAL_SET (dst, SCM_PROGRAM_FREE_VARIABLE_REF (LOCAL_REF (src), idx));
+ SP_SET (dst, SCM_PROGRAM_FREE_VARIABLE_REF (SP_REF (src), idx));
NEXT (2);
}
@@ -1749,7 +1735,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, dst, src);
UNPACK_24 (ip[1], idx);
/* CHECK_FREE_VARIABLE (src); */
- SCM_PROGRAM_FREE_VARIABLE_SET (LOCAL_REF (dst), idx, LOCAL_REF (src));
+ SCM_PROGRAM_FREE_VARIABLE_SET (SP_REF (dst), idx, SP_REF (src));
NEXT (2);
}
@@ -1771,7 +1757,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_bits val;
UNPACK_8_16 (op, dst, val);
- LOCAL_SET (dst, SCM_PACK (val));
+ SP_SET (dst, SCM_PACK (val));
NEXT (1);
}
@@ -1787,7 +1773,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, dst);
val = ip[1];
- LOCAL_SET (dst, SCM_PACK (val));
+ SP_SET (dst, SCM_PACK (val));
NEXT (2);
}
@@ -1809,7 +1795,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
ASSERT (ip[1] == 0);
val = ip[2];
#endif
- LOCAL_SET (dst, SCM_PACK (val));
+ SP_SET (dst, SCM_PACK (val));
NEXT (3);
}
@@ -1840,7 +1826,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
VM_ASSERT (!(unpacked & 0x7), abort());
- LOCAL_SET (dst, SCM_PACK (unpacked));
+ SP_SET (dst, SCM_PACK (unpacked));
NEXT (2);
}
@@ -1868,7 +1854,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
loc_bits = (scm_t_uintptr) loc;
VM_ASSERT (ALIGNED_P (loc, SCM), abort());
- LOCAL_SET (dst, *((SCM *) loc_bits));
+ SP_SET (dst, *((SCM *) loc_bits));
NEXT (2);
}
@@ -1889,7 +1875,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
loc = ip + offset;
VM_ASSERT (ALIGNED_P (loc, SCM), abort());
- *((SCM *) loc) = LOCAL_REF (src);
+ *((SCM *) loc) = SP_REF (src);
NEXT (2);
}
@@ -1965,7 +1951,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (op, dst);
SYNC_IP ();
- LOCAL_SET (dst, scm_current_module ());
+ SP_SET (dst, scm_current_module ());
NEXT (1);
}
@@ -1985,11 +1971,11 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (ip[1], sym);
SYNC_IP ();
- var = scm_lookup (LOCAL_REF (sym));
- CACHE_LOCALS ();
+ var = scm_lookup (SP_REF (sym));
+ CACHE_SP ();
if (ip[1] & 0x1)
- VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (LOCAL_REF (sym)));
- LOCAL_SET (dst, var);
+ VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (SP_REF (sym)));
+ SP_SET (dst, var);
NEXT (2);
}
@@ -2004,8 +1990,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 sym, val;
UNPACK_12_12 (op, sym, val);
SYNC_IP ();
- scm_define (LOCAL_REF (sym), LOCAL_REF (val));
- CACHE_LOCALS ();
+ scm_define (SP_REF (sym), SP_REF (val));
+ CACHE_SP ();
NEXT (1);
}
@@ -2065,14 +2051,14 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
mod = scm_the_root_module ();
var = scm_module_lookup (mod, sym);
- CACHE_LOCALS ();
+ CACHE_SP ();
if (ip[4] & 0x1)
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (sym));
*var_loc = var;
}
- LOCAL_SET (dst, var);
+ SP_SET (dst, var);
NEXT (5);
}
@@ -2126,7 +2112,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
else
var = scm_private_lookup (SCM_CDR (modname), sym);
- CACHE_LOCALS ();
+ CACHE_SP ();
if (ip[4] & 0x1)
VM_ASSERT (VARIABLE_BOUNDP (var), vm_error_unbound (sym));
@@ -2134,7 +2120,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
*var_loc = var;
}
- LOCAL_SET (dst, var);
+ SP_SET (dst, var);
NEXT (5);
}
@@ -2167,9 +2153,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
/* Push the prompt onto the dynamic stack. */
flags = escape_only_p ? SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY : 0;
scm_dynstack_push_prompt (&thread->dynstack, flags,
- LOCAL_REF (tag),
+ SP_REF (tag),
vp->stack_top - vp->fp,
- vp->stack_top - LOCAL_ADDRESS (proc_slot),
+ vp->stack_top - FP_SLOT (proc_slot),
ip + offset,
registers);
NEXT (3);
@@ -2188,7 +2174,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 winder, unwinder;
UNPACK_12_12 (op, winder, unwinder);
scm_dynstack_push_dynwind (&thread->dynstack,
- LOCAL_REF (winder), LOCAL_REF (unwinder));
+ SP_REF (winder), SP_REF (unwinder));
NEXT (1);
}
@@ -2214,7 +2200,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, fluid, value);
scm_dynstack_push_fluid (&thread->dynstack,
- LOCAL_REF (fluid), LOCAL_REF (value),
+ SP_REF (fluid), SP_REF (value),
thread->dynamic_state);
NEXT (1);
}
@@ -2243,14 +2229,14 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SCM fluid, fluids;
UNPACK_12_12 (op, dst, src);
- fluid = LOCAL_REF (src);
+ fluid = SP_REF (src);
fluids = SCM_I_DYNAMIC_STATE_FLUIDS (thread->dynamic_state);
if (SCM_UNLIKELY (!SCM_FLUID_P (fluid))
|| ((num = SCM_I_FLUID_NUM (fluid)) >= SCM_SIMPLE_VECTOR_LENGTH (fluids)))
{
/* Punt dynstate expansion and error handling to the C proc. */
SYNC_IP ();
- LOCAL_SET (dst, scm_fluid_ref (fluid));
+ SP_SET (dst, scm_fluid_ref (fluid));
}
else
{
@@ -2259,7 +2245,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
val = SCM_I_FLUID_DEFAULT (fluid);
VM_ASSERT (!scm_is_eq (val, SCM_UNDEFINED),
vm_error_unbound_fluid (fluid));
- LOCAL_SET (dst, val);
+ SP_SET (dst, val);
}
NEXT (1);
@@ -2276,17 +2262,17 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SCM fluid, fluids;
UNPACK_12_12 (op, a, b);
- fluid = LOCAL_REF (a);
+ fluid = SP_REF (a);
fluids = SCM_I_DYNAMIC_STATE_FLUIDS (thread->dynamic_state);
if (SCM_UNLIKELY (!SCM_FLUID_P (fluid))
|| ((num = SCM_I_FLUID_NUM (fluid)) >= SCM_SIMPLE_VECTOR_LENGTH (fluids)))
{
/* Punt dynstate expansion and error handling to the C proc. */
SYNC_IP ();
- scm_fluid_set_x (fluid, LOCAL_REF (b));
+ scm_fluid_set_x (fluid, SP_REF (b));
}
else
- SCM_SIMPLE_VECTOR_SET (fluids, num, LOCAL_REF (b));
+ SCM_SIMPLE_VECTOR_SET (fluids, num, SP_REF (b));
NEXT (1);
}
@@ -2347,8 +2333,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, dst, src);
SYNC_IP ();
- LOCAL_SET (dst,
- scm_string_to_number (LOCAL_REF (src),
+ SP_SET (dst,
+ scm_string_to_number (SP_REF (src),
SCM_UNDEFINED /* radix = 10 */));
NEXT (1);
}
@@ -2363,7 +2349,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_12_12 (op, dst, src);
SYNC_IP ();
- LOCAL_SET (dst, scm_string_to_symbol (LOCAL_REF (src)));
+ SP_SET (dst, scm_string_to_symbol (SP_REF (src)));
NEXT (1);
}
@@ -2376,7 +2362,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 dst, src;
UNPACK_12_12 (op, dst, src);
SYNC_IP ();
- LOCAL_SET (dst, scm_symbol_to_keyword (LOCAL_REF (src)));
+ SP_SET (dst, scm_symbol_to_keyword (SP_REF (src)));
NEXT (1);
}
@@ -2427,8 +2413,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 a, b;
SCM x, y;
UNPACK_12_12 (op, a, b);
- x = LOCAL_REF (a);
- y = LOCAL_REF (b);
+ x = SP_REF (a);
+ y = SP_REF (b);
VM_VALIDATE_PAIR (x, "set-car!");
SCM_SETCAR (x, y);
NEXT (1);
@@ -2443,8 +2429,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_uint16 a, b;
SCM x, y;
UNPACK_12_12 (op, a, b);
- x = LOCAL_REF (a);
- y = LOCAL_REF (b);
+ x = SP_REF (a);
+ y = SP_REF (b);
VM_VALIDATE_PAIR (x, "set-car!");
SCM_SETCDR (x, y);
NEXT (1);
@@ -2659,7 +2645,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, length, init);
- LOCAL_SET (dst, scm_make_vector (LOCAL_REF (length), LOCAL_REF (init)));
+ SP_SET (dst, scm_make_vector (SP_REF (length), SP_REF (init)));
NEXT (1);
}
@@ -2678,12 +2664,12 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, length, init);
- val = LOCAL_REF (init);
+ val = SP_REF (init);
vector = scm_inline_words (thread, scm_tc7_vector | (length << 8),
length + 1);
for (n = 0; n < length; n++)
SCM_SIMPLE_VECTOR_SET (vector, n, val);
- LOCAL_SET (dst, vector);
+ SP_SET (dst, vector);
NEXT (1);
}
@@ -2728,12 +2714,12 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SCM v;
UNPACK_8_8_8 (op, dst, src, idx);
- v = LOCAL_REF (src);
+ v = SP_REF (src);
VM_ASSERT (SCM_I_IS_VECTOR (v),
vm_error_not_a_vector ("vector-ref", v));
VM_ASSERT (idx < SCM_I_VECTOR_LENGTH (v),
vm_error_out_of_range ("vector-ref", scm_from_size_t (idx)));
- LOCAL_SET (dst, SCM_I_VECTOR_ELTS (LOCAL_REF (src))[idx]);
+ SP_SET (dst, SCM_I_VECTOR_ELTS (SP_REF (src))[idx]);
NEXT (1);
}
@@ -2748,9 +2734,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_signed_bits i = 0;
UNPACK_8_8_8 (op, dst, idx_var, src);
- vect = LOCAL_REF (dst);
- idx = LOCAL_REF (idx_var);
- val = LOCAL_REF (src);
+ vect = SP_REF (dst);
+ idx = SP_REF (idx_var);
+ val = SP_REF (src);
VM_ASSERT (SCM_I_IS_VECTOR (vect),
vm_error_not_a_vector ("vector-ref", vect));
@@ -2773,8 +2759,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SCM vect, val;
UNPACK_8_8_8 (op, dst, idx, src);
- vect = LOCAL_REF (dst);
- val = LOCAL_REF (src);
+ vect = SP_REF (dst);
+ val = SP_REF (src);
VM_ASSERT (SCM_I_IS_VECTOR (vect),
vm_error_not_a_vector ("vector-ref", vect));
@@ -2816,8 +2802,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, vtable, nfields);
SYNC_IP ();
- ret = scm_allocate_struct (LOCAL_REF (vtable), LOCAL_REF (nfields));
- LOCAL_SET (dst, ret);
+ ret = scm_allocate_struct (SP_REF (vtable), SP_REF (nfields));
+ SP_SET (dst, ret);
NEXT (1);
}
@@ -2835,8 +2821,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, src, idx);
- obj = LOCAL_REF (src);
- index = LOCAL_REF (idx);
+ obj = SP_REF (src);
+ index = SP_REF (idx);
if (SCM_LIKELY (SCM_STRUCTP (obj)
&& SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
@@ -2863,9 +2849,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, idx, src);
- obj = LOCAL_REF (dst);
- val = LOCAL_REF (src);
- index = LOCAL_REF (idx);
+ obj = SP_REF (dst);
+ val = SP_REF (src);
+ index = SP_REF (idx);
if (SCM_LIKELY (SCM_STRUCTP (obj)
&& SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
@@ -2901,8 +2887,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, vtable, nfields);
SYNC_IP ();
- ret = scm_allocate_struct (LOCAL_REF (vtable), SCM_I_MAKINUM (nfields));
- LOCAL_SET (dst, ret);
+ ret = scm_allocate_struct (SP_REF (vtable), SCM_I_MAKINUM (nfields));
+ SP_SET (dst, ret);
NEXT (1);
}
@@ -2919,7 +2905,7 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, src, idx);
- obj = LOCAL_REF (src);
+ obj = SP_REF (src);
if (SCM_LIKELY (SCM_STRUCTP (obj)
&& SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
@@ -2944,8 +2930,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_8_8_8 (op, dst, idx, src);
- obj = LOCAL_REF (dst);
- val = LOCAL_REF (src);
+ obj = SP_REF (dst);
+ val = SP_REF (src);
if (SCM_LIKELY (SCM_STRUCTP (obj)
&& SCM_STRUCT_VTABLE_FLAG_IS_SET (obj,
@@ -3001,8 +2987,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
offset = ip[3];
len = ip[4];
SYNC_IP ();
- LOCAL_SET (dst, scm_from_contiguous_typed_array (LOCAL_REF (type),
- LOCAL_REF (shape),
+ SP_SET (dst, scm_from_contiguous_typed_array (SP_REF (type),
+ SP_REF (shape),
ip + offset, len));
NEXT (5);
}
@@ -3019,8 +3005,8 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
UNPACK_24 (ip[2], fill);
UNPACK_24 (ip[3], bounds);
SYNC_IP ();
- LOCAL_SET (dst, scm_make_typed_array (LOCAL_REF (type), LOCAL_REF (fill),
- LOCAL_REF (bounds)));
+ SP_SET (dst, scm_make_typed_array (SP_REF (type), SP_REF (fill),
+ SP_REF (bounds)));
NEXT (4);
}
@@ -3171,9 +3157,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_ ## type *int_ptr; \
\
UNPACK_8_8_8 (op, dst, idx, src); \
- bv = LOCAL_REF (dst); \
- scm_idx = LOCAL_REF (idx); \
- val = LOCAL_REF (src); \
+ bv = SP_REF (dst); \
+ scm_idx = SP_REF (idx); \
+ val = SP_REF (src); \
VM_VALIDATE_BYTEVECTOR (bv, "bv-" #stem "-set!"); \
i = SCM_I_INUM (scm_idx); \
int_ptr = (scm_t_ ## type *) (SCM_BYTEVECTOR_CONTENTS (bv) + i); \
@@ -3202,9 +3188,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
scm_t_ ## type *int_ptr; \
\
UNPACK_8_8_8 (op, dst, idx, src); \
- bv = LOCAL_REF (dst); \
- scm_idx = LOCAL_REF (idx); \
- val = LOCAL_REF (src); \
+ bv = SP_REF (dst); \
+ scm_idx = SP_REF (idx); \
+ val = SP_REF (src); \
VM_VALIDATE_BYTEVECTOR (bv, "bv-" #stem "-set!"); \
i = SCM_I_INUM (scm_idx); \
int_ptr = (scm_t_ ## type *) (SCM_BYTEVECTOR_CONTENTS (bv) + i); \
@@ -3230,9 +3216,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
type *float_ptr; \
\
UNPACK_8_8_8 (op, dst, idx, src); \
- bv = LOCAL_REF (dst); \
- scm_idx = LOCAL_REF (idx); \
- val = LOCAL_REF (src); \
+ bv = SP_REF (dst); \
+ scm_idx = SP_REF (idx); \
+ val = SP_REF (src); \
VM_VALIDATE_BYTEVECTOR (bv, "bv-" #stem "-set!"); \
i = SCM_I_INUM (scm_idx); \
float_ptr = (type *) (SCM_BYTEVECTOR_CONTENTS (bv) + i); \
@@ -3440,8 +3426,11 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
#undef INIT
#undef INUM_MAX
#undef INUM_MIN
-#undef LOCAL_REF
-#undef LOCAL_SET
+#undef FP_REF
+#undef FP_SET
+#undef FP_SLOT
+#undef SP_REF
+#undef SP_SET
#undef NEXT
#undef NEXT_HOOK
#undef NEXT_JUMP
diff --git a/libguile/vm.c b/libguile/vm.c
index 2db079550..9d9cc3129 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -649,9 +649,9 @@ static const scm_t_uint32 vm_builtin_abort_to_prompt_code[] = {
static const scm_t_uint32 vm_builtin_call_with_values_code[] = {
SCM_PACK_OP_24 (assert_nargs_ee, 3),
SCM_PACK_OP_24 (alloc_frame, 7),
- SCM_PACK_OP_12_12 (mov, 6, 1),
+ SCM_PACK_OP_12_12 (mov, 0, 5),
SCM_PACK_OP_24 (call, 6), SCM_PACK_OP_ARG_8_24 (0, 1),
- SCM_PACK_OP_12_12 (mov, 0, 2),
+ SCM_PACK_OP_24 (long_fmov, 0), SCM_PACK_OP_ARG_8_24 (0, 2),
SCM_PACK_OP_24 (tail_call_shuffle, 7)
};
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index 498bac9b3..5b0c32990 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -100,9 +100,12 @@
(define (constant sym)
(lookup-constant-value sym allocation))
+ (define (from-sp var)
+ (- frame-size 1 var))
+
(define (maybe-mov dst src)
(unless (= dst src)
- (emit-mov asm dst src)))
+ (emit-mov asm (from-sp dst) (from-sp src))))
(define (compile-tail label exp)
;; There are only three kinds of expressions in tail position:
@@ -110,12 +113,12 @@
(match exp
(($ $call proc args)
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation))
(emit-tail-call asm (1+ (length args))))
(($ $callk k proc args)
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation))
(emit-tail-call-label asm (1+ (length args)) k))
(($ $values ())
@@ -123,83 +126,109 @@
(emit-return-values asm))
(($ $values (arg))
(if (maybe-slot arg)
- (emit-return asm (slot arg))
+ (emit-return asm (from-sp (slot arg)))
(begin
- (emit-load-constant asm 1 (constant arg))
- (emit-return asm 1))))
+ (when (< frame-size 2)
+ (emit-alloc-frame asm 2))
+ (emit-load-constant asm (from-sp 1) (constant arg))
+ (emit-return asm (from-sp 1)))))
(($ $values args)
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation))
(emit-reset-frame asm (1+ (length args)))
(emit-return-values asm))
(($ $primcall 'return (arg))
- (emit-return asm (slot arg)))))
+ (emit-return asm (from-sp (slot arg))))))
(define (compile-value label exp dst)
(match exp
(($ $values (arg))
(maybe-mov dst (slot arg)))
(($ $const exp)
- (emit-load-constant asm dst exp))
+ (emit-load-constant asm (from-sp dst) exp))
(($ $closure k 0)
- (emit-load-static-procedure asm dst k))
+ (emit-load-static-procedure asm (from-sp dst) k))
(($ $closure k nfree)
- (emit-make-closure asm dst k nfree))
+ (emit-make-closure asm (from-sp dst) k nfree))
(($ $primcall 'current-module)
- (emit-current-module asm dst))
+ (emit-current-module asm (from-sp dst)))
(($ $primcall 'cached-toplevel-box (scope name bound?))
- (emit-cached-toplevel-box asm dst (constant scope) (constant name)
+ (emit-cached-toplevel-box asm (from-sp dst)
+ (constant scope) (constant name)
(constant bound?)))
(($ $primcall 'cached-module-box (mod name public? bound?))
- (emit-cached-module-box asm dst (constant mod) (constant name)
+ (emit-cached-module-box asm (from-sp dst)
+ (constant mod) (constant name)
(constant public?) (constant bound?)))
(($ $primcall 'resolve (name bound?))
- (emit-resolve asm dst (constant bound?) (slot name)))
+ (emit-resolve asm (from-sp dst) (constant bound?)
+ (from-sp (slot name))))
(($ $primcall 'free-ref (closure idx))
- (emit-free-ref asm dst (slot closure) (constant idx)))
+ (emit-free-ref asm (from-sp dst) (from-sp (slot closure))
+ (constant idx)))
(($ $primcall 'vector-ref (vector index))
- (emit-vector-ref asm dst (slot vector) (slot index)))
+ (emit-vector-ref asm (from-sp dst) (from-sp (slot vector))
+ (from-sp (slot index))))
(($ $primcall 'make-vector (length init))
- (emit-make-vector asm dst (slot length) (slot init)))
+ (emit-make-vector asm (from-sp dst) (from-sp (slot length))
+ (from-sp (slot init))))
(($ $primcall 'make-vector/immediate (length init))
- (emit-make-vector/immediate asm dst (constant length) (slot init)))
+ (emit-make-vector/immediate asm (from-sp dst) (constant length)
+ (from-sp (slot init))))
(($ $primcall 'vector-ref/immediate (vector index))
- (emit-vector-ref/immediate asm dst (slot vector) (constant index)))
+ (emit-vector-ref/immediate asm (from-sp dst) (from-sp (slot vector))
+ (constant index)))
(($ $primcall 'allocate-struct (vtable nfields))
- (emit-allocate-struct asm dst (slot vtable) (slot nfields)))
+ (emit-allocate-struct asm (from-sp dst) (from-sp (slot vtable))
+ (from-sp (slot nfields))))
(($ $primcall 'allocate-struct/immediate (vtable nfields))
- (emit-allocate-struct/immediate asm dst (slot vtable) (constant nfields)))
+ (emit-allocate-struct/immediate asm (from-sp dst)
+ (from-sp (slot vtable))
+ (constant nfields)))
(($ $primcall 'struct-ref (struct n))
- (emit-struct-ref asm dst (slot struct) (slot n)))
+ (emit-struct-ref asm (from-sp dst) (from-sp (slot struct))
+ (from-sp (slot n))))
(($ $primcall 'struct-ref/immediate (struct n))
- (emit-struct-ref/immediate asm dst (slot struct) (constant n)))
+ (emit-struct-ref/immediate asm (from-sp dst) (from-sp (slot struct))
+ (constant n)))
(($ $primcall 'builtin-ref (name))
- (emit-builtin-ref asm dst (constant name)))
+ (emit-builtin-ref asm (from-sp dst) (constant name)))
(($ $primcall 'bv-u8-ref (bv idx))
- (emit-bv-u8-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-u8-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-s8-ref (bv idx))
- (emit-bv-s8-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-s8-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-u16-ref (bv idx))
- (emit-bv-u16-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-u16-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-s16-ref (bv idx))
- (emit-bv-s16-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-s16-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-u32-ref (bv idx val))
- (emit-bv-u32-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-u32-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-s32-ref (bv idx val))
- (emit-bv-s32-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-s32-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-u64-ref (bv idx val))
- (emit-bv-u64-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-u64-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-s64-ref (bv idx val))
- (emit-bv-s64-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-s64-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-f32-ref (bv idx val))
- (emit-bv-f32-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-f32-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall 'bv-f64-ref (bv idx val))
- (emit-bv-f64-ref asm dst (slot bv) (slot idx)))
+ (emit-bv-f64-ref asm (from-sp dst) (from-sp (slot bv))
+ (from-sp (slot idx))))
(($ $primcall name args)
;; FIXME: Inline all the cases.
(let ((inst (prim-instruction name)))
- (emit-text asm `((,inst ,dst ,@(map slot args))))))))
+ (emit-text asm `((,inst ,(from-sp dst)
+ ,@(map (compose from-sp slot) args))))))))
(define (compile-effect label exp k)
(match exp
@@ -210,7 +239,8 @@
(let ((receive-args (gensym "handler"))
(nreq (length req))
(proc-slot (lookup-call-proc-slot label allocation)))
- (emit-prompt asm (slot tag) escape? proc-slot receive-args)
+ (emit-prompt asm (from-sp (slot tag)) escape? proc-slot
+ receive-args)
(emit-br asm k)
(emit-label asm receive-args)
(unless (and rest (zero? nreq))
@@ -221,57 +251,71 @@
(maybe-slot rest))))
(emit-bind-rest asm (+ proc-slot 1 nreq)))
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-fmov asm dst src)))
(lookup-parallel-moves handler allocation))
(emit-reset-frame asm frame-size)
(emit-br asm (forward-label khandler-body))))))
(($ $primcall 'cache-current-module! (sym scope))
- (emit-cache-current-module! asm (slot sym) (constant scope)))
+ (emit-cache-current-module! asm (from-sp (slot sym)) (constant scope)))
(($ $primcall 'free-set! (closure idx value))
- (emit-free-set! asm (slot closure) (slot value) (constant idx)))
+ (emit-free-set! asm (from-sp (slot closure)) (from-sp (slot value))
+ (constant idx)))
(($ $primcall 'box-set! (box value))
- (emit-box-set! asm (slot box) (slot value)))
+ (emit-box-set! asm (from-sp (slot box)) (from-sp (slot value))))
(($ $primcall 'struct-set! (struct index value))
- (emit-struct-set! asm (slot struct) (slot index) (slot value)))
+ (emit-struct-set! asm (from-sp (slot struct)) (from-sp (slot index))
+ (from-sp (slot value))))
(($ $primcall 'struct-set!/immediate (struct index value))
- (emit-struct-set!/immediate asm (slot struct) (constant index) (slot value)))
+ (emit-struct-set!/immediate asm (from-sp (slot struct))
+ (constant index) (from-sp (slot value))))
(($ $primcall 'vector-set! (vector index value))
- (emit-vector-set! asm (slot vector) (slot index) (slot value)))
+ (emit-vector-set! asm (from-sp (slot vector)) (from-sp (slot index))
+ (from-sp (slot value))))
(($ $primcall 'vector-set!/immediate (vector index value))
- (emit-vector-set!/immediate asm (slot vector) (constant index)
- (slot value)))
+ (emit-vector-set!/immediate asm (from-sp (slot vector))
+ (constant index) (from-sp (slot value))))
(($ $primcall 'set-car! (pair value))
- (emit-set-car! asm (slot pair) (slot value)))
+ (emit-set-car! asm (from-sp (slot pair)) (from-sp (slot value))))
(($ $primcall 'set-cdr! (pair value))
- (emit-set-cdr! asm (slot pair) (slot value)))
+ (emit-set-cdr! asm (from-sp (slot pair)) (from-sp (slot value))))
(($ $primcall 'define! (sym value))
- (emit-define! asm (slot sym) (slot value)))
+ (emit-define! asm (from-sp (slot sym)) (from-sp (slot value))))
(($ $primcall 'push-fluid (fluid val))
- (emit-push-fluid asm (slot fluid) (slot val)))
+ (emit-push-fluid asm (from-sp (slot fluid)) (from-sp (slot val))))
(($ $primcall 'pop-fluid ())
(emit-pop-fluid asm))
(($ $primcall 'wind (winder unwinder))
- (emit-wind asm (slot winder) (slot unwinder)))
+ (emit-wind asm (from-sp (slot winder)) (from-sp (slot unwinder))))
(($ $primcall 'bv-u8-set! (bv idx val))
- (emit-bv-u8-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-u8-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-s8-set! (bv idx val))
- (emit-bv-s8-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-s8-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-u16-set! (bv idx val))
- (emit-bv-u16-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-u16-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-s16-set! (bv idx val))
- (emit-bv-s16-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-s16-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-u32-set! (bv idx val))
- (emit-bv-u32-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-u32-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-s32-set! (bv idx val))
- (emit-bv-s32-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-s32-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-u64-set! (bv idx val))
- (emit-bv-u64-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-u64-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-s64-set! (bv idx val))
- (emit-bv-s64-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-s64-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-f32-set! (bv idx val))
- (emit-bv-f32-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-f32-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'bv-f64-set! (bv idx val))
- (emit-bv-f64-set! asm (slot bv) (slot idx) (slot val)))
+ (emit-bv-f64-set! asm (from-sp (slot bv)) (from-sp (slot idx))
+ (from-sp (slot val))))
(($ $primcall 'unwind ())
(emit-unwind asm))))
@@ -279,7 +323,7 @@
(match exp
(($ $values args)
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation)))))
(define (compile-test label exp kt kf next-label)
@@ -294,22 +338,23 @@
(define (unary op sym)
(cond
((eq? kt next-label)
- (op asm (slot sym) #t kf))
+ (op asm (from-sp (slot sym)) #t kf))
((eq? kf next-label)
- (op asm (slot sym) #f kt))
+ (op asm (from-sp (slot sym)) #f kt))
(else
(let ((invert? (not (prefer-true?))))
- (op asm (slot sym) invert? (if invert? kf kt))
+ (op asm (from-sp (slot sym)) invert? (if invert? kf kt))
(emit-br asm (if invert? kt kf))))))
(define (binary op a b)
(cond
((eq? kt next-label)
- (op asm (slot a) (slot b) #t kf))
+ (op asm (from-sp (slot a)) (from-sp (slot b)) #t kf))
((eq? kf next-label)
- (op asm (slot a) (slot b) #f kt))
+ (op asm (from-sp (slot a)) (from-sp (slot b)) #f kt))
(else
(let ((invert? (not (prefer-true?))))
- (op asm (slot a) (slot b) invert? (if invert? kf kt))
+ (op asm (from-sp (slot a)) (from-sp (slot b)) invert?
+ (if invert? kf kt))
(emit-br asm (if invert? kt kf))))))
(match exp
(($ $values (sym)) (unary emit-br-if-true sym))
@@ -344,7 +389,7 @@
(nargs (1+ (length args)))
(arg-slots (map (lambda (x) (+ x proc-slot)) (iota nargs))))
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-mov asm (from-sp dst) (from-sp src))))
(lookup-parallel-moves label allocation))
(emit-call asm proc-slot nargs)
(emit-dead-slot-map asm proc-slot
@@ -365,7 +410,7 @@
(when (and rest-var (maybe-slot rest-var))
(emit-bind-rest asm (+ proc-slot 1 nreq)))
(for-each (match-lambda
- ((src . dst) (emit-mov asm dst src)))
+ ((src . dst) (emit-fmov asm dst src)))
(lookup-parallel-moves k allocation))
(emit-reset-frame asm frame-size)))))
(match exp
diff --git a/module/language/cps/slot-allocation.scm b/module/language/cps/slot-allocation.scm
index 8be36e716..b3068985c 100644
--- a/module/language/cps/slot-allocation.scm
+++ b/module/language/cps/slot-allocation.scm
@@ -537,12 +537,6 @@ are comparable with eqv?. A tmp slot may be used."
;; could be that they are out of the computed live set. In that case
;; they need to be adjoined to the live set, used when choosing a
;; temporary slot.
- ;;
- ;; Note that although we reserve slots 253-255 for shuffling operands
- ;; that address less than the full 24-bit range of locals, that
- ;; reservation doesn't apply here, because this temporary itself is
- ;; used while doing parallel assignment via "mov", and "mov" does not
- ;; need shuffling.
(define (compute-tmp-slot live stack-slots)
(find-first-zero (fold add-live-slot live stack-slots)))
@@ -687,10 +681,9 @@ are comparable with eqv?. A tmp slot may be used."
(match vars
(() slots)
((var . vars)
- (let ((n (if (<= 253 n 255) 256 n)))
- (lp vars
- (intmap-add! slots var n)
- (1+ n)))))))))
+ (lp vars
+ (intmap-add! slots var n)
+ (1+ n))))))))
(_ slots)))
cps empty-intmap))
@@ -701,15 +694,9 @@ are comparable with eqv?. A tmp slot may be used."
(logand live-slots (lognot (ash 1 slot))))
(define-inlinable (compute-slot live-slots hint)
- ;; Slots 253-255 are reserved for shuffling; see comments in
- ;; assembler.scm.
- (if (and hint (not (logbit? hint live-slots))
- (or (< hint 253) (> hint 255)))
+ (if (and hint (not (logbit? hint live-slots)))
hint
- (let ((slot (find-first-zero live-slots)))
- (if (or (< slot 253) (> slot 255))
- slot
- (+ 256 (find-first-zero (ash live-slots -256)))))))
+ (find-first-zero live-slots)))
(define (allocate-lazy-vars cps slots call-allocs live-in lazy)
(define (compute-live-slots slots label)
diff --git a/module/system/vm/assembler.scm b/module/system/vm/assembler.scm
index f29105108..bad298d0d 100644
--- a/module/system/vm/assembler.scm
+++ b/module/system/vm/assembler.scm
@@ -97,6 +97,7 @@
emit-br-if-<=
emit-br-if-logtest
(emit-mov* . emit-mov)
+ (emit-fmov* . emit-fmov)
(emit-box* . emit-box)
(emit-box-ref* . emit-box-ref)
(emit-box-set!* . emit-box-set!)
@@ -638,166 +639,170 @@ later by the linker."
(eval-when (expand)
- ;; Some operands are encoded using a restricted subset of the full
- ;; 24-bit local address space, in order to make the bytecode more
- ;; dense in the usual case that there are few live locals. Here we
- ;; define wrapper emitters that shuffle out-of-range operands into and
- ;; out of the reserved range of locals [233,255]. This range is
- ;; sufficient because these restricted operands are only present in
- ;; the first word of an instruction. Since 8 bits is the smallest
- ;; slot-addressing operand size, that means we can fit 3 operands in
- ;; the 24 bits of payload of the first word (the lower 8 bits being
- ;; taken by the opcode).
+ ;; In Guile's VM, locals are usually addressed via the stack pointer
+ ;; (SP). There can be up to 2^24 slots for local variables in a
+ ;; frame. Some instructions encode their operands using a restricted
+ ;; subset of the full 24-bit local address space, in order to make the
+ ;; bytecode more dense in the usual case that a function needs few
+ ;; local slots. To allow these instructions to be used when there are
+ ;; many local slots, we can temporarily push the values on the stack,
+ ;; operate on them there, and then store back any result as we pop the
+ ;; SP to its original position.
;;
- ;; The result are wrapper emitters with the same arity,
- ;; e.g. emit-cons* that wraps emit-cons. We expose these wrappers as
- ;; the public interface for emitting `cons' instructions. That way we
- ;; solve the problem fully and in just one place. The only manual
- ;; care that need be taken is in the exports list at the top of the
- ;; file -- to be sure that we export the wrapper and not the wrapped
- ;; emitter.
+ ;; We implement this shuffling via wrapper emitters that have the same
+ ;; arity as the emitter they wrap, e.g. emit-cons* that wraps
+ ;; emit-cons. We expose these wrappers as the public interface for
+ ;; emitting `cons' instructions. That way we solve the problem fully
+ ;; and in just one place. The only manual care that need be taken is
+ ;; in the exports list at the top of the file -- to be sure that we
+ ;; export the wrapper and not the wrapped emitter.
- (define (shuffling-assembler name kind word0 word*)
- (define (analyze-first-word)
- (define-syntax op-case
- (syntax-rules ()
- ((_ type ((%type %kind arg ...) values) clause ...)
- (if (and (eq? type '%type) (eq? kind '%kind))
- (with-syntax (((arg ...) (generate-temporaries #'(arg ...))))
- #'((arg ...) values))
- (op-case type clause ...)))
- ((_ type)
- #f)))
- (op-case
- word0
- ((X8_S8_I16 <- a imm)
- (values (if (< a (ash 1 8)) a 253)
- imm))
- ((X8_S12_S12 ! a b)
- (values (if (< a (ash 1 12)) a (begin (emit-mov* asm 253 a) 253))
- (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
- ((X8_S12_S12 <- a b)
- (values (if (< a (ash 1 12)) a 253)
- (if (< b (ash 1 12)) b (begin (emit-mov* asm 254 b) 254))))
- ((X8_S12_C12 <- a b)
- (values (if (< a (ash 1 12)) a 253)
- b))
+ (define (shuffling-assembler emit kind word0 word*)
+ (with-syntax ((emit emit))
+ (match (cons* word0 kind word*)
+ (('X8_S12_S12 '!)
+ #'(lambda (asm a b)
+ (cond
+ ((< (logior a b) (ash 1 12))
+ (emit asm a b))
+ (else
+ (emit-push asm a)
+ (emit-push asm (1+ b))
+ (emit asm 1 0)
+ (emit-drop asm 2)))))
+ (('X8_S12_S12 '<-)
+ #'(lambda (asm dst a)
+ (cond
+ ((< (logior dst a) (ash 1 12))
+ (emit asm dst a))
+ (else
+ (emit-push asm a)
+ (emit asm 0 0)
+ (emit-pop asm dst)))))
- ((X8_S8_S8_S8 ! a b c)
- (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
- (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
- (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
- ((X8_S8_S8_S8 <- a b c)
- (values (if (< a (ash 1 8)) a 253)
- (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
- (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
+ (('X8_S12_S12 '! 'X8_C24)
+ #'(lambda (asm a b c)
+ (cond
+ ((< (logior a b) (ash 1 12))
+ (emit asm a b c))
+ (else
+ (emit-push asm a)
+ (emit-push asm (1+ b))
+ (emit asm 1 0 c)
+ (emit-drop asm 2)))))
+ (('X8_S12_S12 '<- 'X8_C24)
+ #'(lambda (asm dst a c)
+ (cond
+ ((< (logior dst a) (ash 1 12))
+ (emit asm dst a c))
+ (else
+ (emit-push asm a)
+ (emit asm 0 0 c)
+ (emit-pop asm dst)))))
- ((X8_S8_S8_C8 ! a b c)
- (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
- (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
- c))
- ((X8_S8_S8_C8 <- a b c)
- (values (if (< a (ash 1 8)) a 253)
- (if (< b (ash 1 8)) b (begin (emit-mov* asm 254 b) 254))
- c))
+ (('X8_S12_C12 '<-)
+ #'(lambda (asm dst const)
+ (cond
+ ((< dst (ash 1 12))
+ (emit asm dst const))
+ (else
+ ;; Push garbage value to make space for dst.
+ (emit-push asm dst)
+ (emit asm 0 const)
+ (emit-pop asm dst)))))
- ((X8_S8_C8_S8 ! a b c)
- (values (if (< a (ash 1 8)) a (begin (emit-mov* asm 253 a) 253))
- b
- (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))
- ((X8_S8_C8_S8 <- a b c)
- (values (if (< a (ash 1 8)) a 253)
- b
- (if (< c (ash 1 8)) c (begin (emit-mov* asm 255 c) 255))))))
+ (('X8_S8_I16 '<-)
+ #'(lambda (asm dst imm)
+ (cond
+ ((< dst (ash 1 8))
+ (emit asm dst imm))
+ (else
+ ;; Push garbage value to make space for dst.
+ (emit-push asm dst)
+ (emit asm 0 imm)
+ (emit-pop asm dst)))))
- (define (tail-formals type)
- (define-syntax op-case
- (syntax-rules ()
- ((op-case type (%type arg ...) clause ...)
- (if (eq? type '%type)
- (generate-temporaries #'(arg ...))
- (op-case type clause ...)))
- ((op-case type)
- (error "unmatched type" type))))
- (op-case type
- (C32 a)
- (I32 imm)
- (A32 imm)
- (B32)
- (N32 label)
- (R32 label)
- (L32 label)
- (LO32 label offset)
- (C8_C24 a b)
- (B1_C7_L24 a b label)
- (B1_X7_S24 a b)
- (B1_X7_F24 a b)
- (B1_X7_C24 a b)
- (B1_X7_L24 a label)
- (B1_X31 a)
- (X8_S24 a)
- (X8_F24 a)
- (X8_C24 a)
- (X8_L24 label)))
+ (('X8_S8_S8_S8 '!)
+ #'(lambda (asm a b c)
+ (cond
+ ((< (logior a b c) (ash 1 8))
+ (emit asm a b c))
+ (else
+ (emit-push asm a)
+ (emit-push asm (+ b 1))
+ (emit-push asm (+ c 2))
+ (emit asm 2 1 0)
+ (emit-drop asm 3)))))
+ (('X8_S8_S8_S8 '<-)
+ #'(lambda (asm dst a b)
+ (cond
+ ((< (logior dst a b) (ash 1 8))
+ (emit asm dst a b))
+ (else
+ (emit-push asm a)
+ (emit-push asm (1+ b))
+ (emit asm 1 1 0)
+ (emit-drop asm 1)
+ (emit-pop asm dst)))))
- (define (shuffle-up dst)
- (define-syntax op-case
- (syntax-rules ()
- ((_ type ((%type ...) exp) clause ...)
- (if (memq type '(%type ...))
- #'exp
- (op-case type clause ...)))
- ((_ type)
- (error "unexpected type" type))))
- (with-syntax ((dst dst))
- (op-case
- word0
- ((X8_S8_I16 X8_S8_S8_S8 X8_S8_S8_C8 X8_S8_C8_S8)
- (unless (< dst (ash 1 8))
- (emit-mov* asm dst 253)))
- ((X8_S12_S12 X8_S12_C12)
- (unless (< dst (ash 1 12))
- (emit-mov* asm dst 253))))))
+ (('X8_S8_S8_C8 '<-)
+ #'(lambda (asm dst a const)
+ (cond
+ ((< (logior dst a) (ash 1 8))
+ (emit asm dst a const))
+ (else
+ (emit-push asm a)
+ (emit asm 0 0 const)
+ (emit-pop asm dst)))))
- (and=>
- (analyze-first-word)
- (lambda (formals+shuffle)
- (with-syntax ((emit-name (id-append name #'emit- name))
- (((formal0 ...) shuffle) formals+shuffle)
- (((formal* ...) ...) (map tail-formals word*)))
- (with-syntax (((shuffle-up-dst ...)
- (if (eq? kind '<-)
- (syntax-case #'(formal0 ...) ()
- ((dst . _)
- (list (shuffle-up #'dst))))
- '())))
- #'(lambda (asm formal0 ... formal* ... ...)
- (call-with-values (lambda () shuffle)
- (lambda (formal0 ...)
- (emit-name asm formal0 ... formal* ... ...)))
- shuffle-up-dst ...))))))
+ (('X8_S8_C8_S8 '!)
+ #'(lambda (asm a const b)
+ (cond
+ ((< (logior a b) (ash 1 8))
+ (emit asm a const b))
+ (else
+ (emit-push asm a)
+ (emit-push asm (1+ b))
+ (emit asm 1 const 0)
+ (emit-drop asm 2)))))
+ (('X8_S8_C8_S8 '<-)
+ #'(lambda (asm dst const a)
+ (cond
+ ((< (logior dst a) (ash 1 8))
+ (emit asm dst const a))
+ (else
+ (emit-push asm a)
+ (emit asm 0 const 0)
+ (emit-pop asm dst))))))))
(define-syntax define-shuffling-assembler
(lambda (stx)
+ (define (might-shuffle? word0)
+ (case word0
+ ((X8_S12_S12 X8_S12_C12
+ X8_S8_I16
+ X8_S8_S8_S8 X8_S8_S8_C8 X8_S8_C8_S8) #t)
+ (else #f)))
+
(syntax-case stx ()
((_ #:except (except ...) name opcode kind word0 word* ...)
- (cond
- ((or-map (lambda (op) (eq? (syntax->datum #'name) op))
- (map syntax->datum #'(except ...)))
- #'(begin))
- ((shuffling-assembler #'name (syntax->datum #'kind)
- (syntax->datum #'word0)
- (map syntax->datum #'(word* ...)))
- => (lambda (proc)
- (with-syntax ((emit (id-append #'name
- (id-append #'name #'emit- #'name)
- #'*))
- (proc proc))
- #'(define emit
- (let ((emit proc))
- (hashq-set! assemblers 'name emit)
- emit)))))
- (else #'(begin))))))))
+ (let ((_except (syntax->datum #'(except ...)))
+ (_name (syntax->datum #'name))
+ (_kind (syntax->datum #'kind))
+ (_word0 (syntax->datum #'word0))
+ (_word* (syntax->datum #'(word* ...)))
+ (emit (id-append #'name #'emit- #'name)))
+ (cond
+ ((and (might-shuffle? _word0) (not (memq _name _except)))
+ (with-syntax
+ ((emit* (id-append #'name emit #'*))
+ (proc (shuffling-assembler emit _kind _word0 _word*)))
+ #'(define emit*
+ (let ((emit* proc))
+ (hashq-set! assemblers 'name emit*)
+ emit*))))
+ (else
+ #'(begin)))))))))
(visit-opcodes define-shuffling-assembler #:except (receive mov))
@@ -809,6 +814,9 @@ later by the linker."
(emit-mov asm dst src)
(emit-long-mov asm dst src)))
+(define (emit-fmov* asm dst src)
+ (emit-long-fmov asm dst src))
+
(define (emit-receive* asm dst proc nlocals)
(if (and (< dst (ash 1 12)) (< proc (ash 1 12)))
(emit-receive asm dst proc nlocals)
@@ -1104,19 +1112,6 @@ returned instead."
(set-arity-definitions! arity (reverse (arity-definitions arity)))
(set-arity-high-pc! arity (asm-start asm))))
-;; As noted above, we reserve locals 253 through 255 for shuffling large
-;; operands. However the calling convention has all arguments passed in
-;; a contiguous block. This helper, called after the clause has been
-;; chosen and the keyword/optional/rest arguments have been processed,
-;; shuffles up arguments from slot 253 and higher into their final
-;; allocations.
-;;
-(define (shuffle-up-args asm nargs)
- (when (> nargs 253)
- (let ((slot (1- nargs)))
- (emit-mov asm (+ slot 3) slot)
- (shuffle-up-args asm (1- nargs)))))
-
(define-macro-assembler (standard-prelude asm nreq nlocals alternate)
(cond
(alternate
@@ -1126,8 +1121,7 @@ returned instead."
(emit-assert-nargs-ee/locals asm nreq (- nlocals nreq)))
(else
(emit-assert-nargs-ee asm nreq)
- (emit-alloc-frame asm nlocals)))
- (shuffle-up-args asm nreq))
+ (emit-alloc-frame asm nlocals))))
(define-macro-assembler (opt-prelude asm nreq nopt rest? nlocals alternate)
(if alternate
@@ -1140,8 +1134,7 @@ returned instead."
(emit-br-if-nargs-gt asm (+ nreq nopt) alternate))
(else
(emit-assert-nargs-le asm (+ nreq nopt))))
- (emit-alloc-frame asm nlocals)
- (shuffle-up-args asm (+ nreq nopt (if rest? 1 0))))
+ (emit-alloc-frame asm nlocals))
(define-macro-assembler (kw-prelude asm nreq nopt rest? kw-indices
allow-other-keys? nlocals alternate)
@@ -1162,8 +1155,7 @@ returned instead."
(+ nreq nopt)
ntotal
(intern-constant asm kw-indices))
- (emit-alloc-frame asm nlocals)
- (shuffle-up-args asm ntotal)))
+ (emit-alloc-frame asm nlocals)))
(define-macro-assembler (label asm sym)
(hashq-set! (asm-labels asm) sym (asm-start asm)))
diff --git a/test-suite/tests/rtl.test b/test-suite/tests/rtl.test
index 082e44fa9..952916963 100644
--- a/test-suite/tests/rtl.test
+++ b/test-suite/tests/rtl.test
@@ -40,8 +40,8 @@ a procedure."
(assemble-program `((begin-program foo
((name . foo)))
(begin-standard-arity () 2 #f)
- (load-constant 1 ,val)
- (return 1)
+ (load-constant 0 ,val)
+ (return 0)
(end-arity)
(end-program))))
@@ -82,15 +82,15 @@ a procedure."
(((assemble-program `((begin-program foo
((name . foo)))
(begin-standard-arity () 2 #f)
- (load-static-procedure 1 bar)
- (return 1)
+ (load-static-procedure 0 bar)
+ (return 0)
(end-arity)
(end-program)
(begin-program bar
((name . bar)))
(begin-standard-arity () 2 #f)
- (load-constant 1 42)
- (return 1)
+ (load-constant 0 42)
+ (return 0)
(end-arity)
(end-program)))))))
@@ -107,16 +107,16 @@ a procedure."
(definition x 1)
(br fix-body)
(label loop-head)
- (br-if-= 2 1 #f out)
- (add 3 2 3)
- (add1 2 2)
+ (br-if-= 1 2 #f out)
+ (add 0 1 0)
+ (add1 1 1)
(br loop-head)
(label fix-body)
- (load-constant 2 0)
- (load-constant 3 0)
+ (load-constant 1 0)
+ (load-constant 0 0)
(br loop-head)
(label out)
- (return 3)
+ (return 0)
(end-arity)
(end-program)))))
(sumto 1000))))
@@ -133,20 +133,20 @@ a procedure."
(begin-standard-arity () 3 #f)
(load-constant 1 0)
(box 1 1)
- (make-closure 2 accum 1)
- (free-set! 2 1 0)
- (return 2)
+ (make-closure 0 accum 1)
+ (free-set! 0 1 0)
+ (return 0)
(end-arity)
(end-program)
(begin-program accum
((name . accum)))
(begin-standard-arity (x) 4 #f)
(definition x 1)
- (free-ref 2 0 0)
- (box-ref 3 2)
- (add 3 3 1)
- (box-set! 2 3)
- (return 3)
+ (free-ref 1 3 0)
+ (box-ref 0 1)
+ (add 0 0 2)
+ (box-set! 1 0)
+ (return 0)
(end-arity)
(end-program)))))
(let ((accum (make-accum)))
@@ -162,10 +162,10 @@ a procedure."
((name . call)))
(begin-standard-arity (f) 7 #f)
(definition f 1)
- (mov 5 1)
+ (mov 1 5)
(call 5 1)
(receive 2 5 7)
- (return 2)
+ (return 4)
(end-arity)
(end-program)))))
(call (lambda () 42))))
@@ -177,11 +177,11 @@ a procedure."
((name . call-with-3)))
(begin-standard-arity (f) 7 #f)
(definition f 1)
- (mov 5 1)
- (load-constant 6 3)
+ (mov 1 5)
+ (load-constant 0 3)
(call 5 2)
(receive 2 5 7)
- (return 2)
+ (return 4)
(end-arity)
(end-program)))))
(call-with-3 (lambda (x) (* x 2))))))
@@ -194,7 +194,7 @@ a procedure."
((name . call)))
(begin-standard-arity (f) 2 #f)
(definition f 1)
- (mov 0 1)
+ (mov 1 0)
(tail-call 1)
(end-arity)
(end-program)))))
@@ -207,8 +207,8 @@ a procedure."
((name . call-with-3)))
(begin-standard-arity (f) 2 #f)
(definition f 1)
- (mov 0 1) ;; R0 <- R1
- (load-constant 1 3) ;; R1 <- 3
+ (mov 1 0) ;; R0 <- R1
+ (load-constant 0 3) ;; R1 <- 3
(tail-call 2)
(end-arity)
(end-program)))))
@@ -221,10 +221,10 @@ a procedure."
'((begin-program get-sqrt-trampoline
((name . get-sqrt-trampoline)))
(begin-standard-arity () 2 #f)
- (current-module 1)
- (cache-current-module! 1 sqrt-scope)
- (load-static-procedure 1 sqrt-trampoline)
- (return 1)
+ (current-module 0)
+ (cache-current-module! 0 sqrt-scope)
+ (load-static-procedure 0 sqrt-trampoline)
+ (return 0)
(end-arity)
(end-program)
@@ -232,8 +232,8 @@ a procedure."
((name . sqrt-trampoline)))
(begin-standard-arity (x) 3 #f)
(definition x 1)
- (cached-toplevel-box 2 sqrt-scope sqrt #t)
- (box-ref 0 2)
+ (cached-toplevel-box 0 sqrt-scope sqrt #t)
+ (box-ref 2 0)
(tail-call 2)
(end-arity)
(end-program)))))
@@ -249,10 +249,10 @@ a procedure."
'((begin-program make-top-incrementor
((name . make-top-incrementor)))
(begin-standard-arity () 2 #f)
- (current-module 1)
- (cache-current-module! 1 top-incrementor)
- (load-static-procedure 1 top-incrementor)
- (return 1)
+ (current-module 0)
+ (cache-current-module! 0 top-incrementor)
+ (load-static-procedure 0 top-incrementor)
+ (return 0)
(end-arity)
(end-program)
@@ -260,9 +260,9 @@ a procedure."
((name . top-incrementor)))
(begin-standard-arity () 3 #f)
(cached-toplevel-box 1 top-incrementor *top-val* #t)
- (box-ref 2 1)
- (add1 2 2)
- (box-set! 1 2)
+ (box-ref 0 1)
+ (add1 0 0)
+ (box-set! 1 0)
(reset-frame 1)
(return-values)
(end-arity)
@@ -277,8 +277,8 @@ a procedure."
'((begin-program get-sqrt-trampoline
((name . get-sqrt-trampoline)))
(begin-standard-arity () 2 #f)
- (load-static-procedure 1 sqrt-trampoline)
- (return 1)
+ (load-static-procedure 0 sqrt-trampoline)
+ (return 0)
(end-arity)
(end-program)
@@ -286,8 +286,8 @@ a procedure."
((name . sqrt-trampoline)))
(begin-standard-arity (x) 3 #f)
(definition x 1)
- (cached-module-box 2 (guile) sqrt #t #t)
- (box-ref 0 2)
+ (cached-module-box 0 (guile) sqrt #t #t)
+ (box-ref 2 0)
(tail-call 2)
(end-arity)
(end-program)))))
@@ -301,8 +301,8 @@ a procedure."
'((begin-program make-top-incrementor
((name . make-top-incrementor)))
(begin-standard-arity () 2 #f)
- (load-static-procedure 1 top-incrementor)
- (return 1)
+ (load-static-procedure 0 top-incrementor)
+ (return 0)
(end-arity)
(end-program)
@@ -310,10 +310,10 @@ a procedure."
((name . top-incrementor)))
(begin-standard-arity () 3 #f)
(cached-module-box 1 (tests bytecode) *top-val* #f #t)
- (box-ref 2 1)
- (add1 2 2)
- (box-set! 1 2)
- (return 2)
+ (box-ref 0 1)
+ (add1 0 0)
+ (box-set! 1 0)
+ (return 0)
(end-arity)
(end-program)))))
((make-top-incrementor))
@@ -323,8 +323,8 @@ a procedure."
(let ((return-3 (assemble-program
'((begin-program return-3 ((name . return-3)))
(begin-standard-arity () 2 #f)
- (load-constant 1 3)
- (return 1)
+ (load-constant 0 3)
+ (return 0)
(end-arity)
(end-program)))))
(pass-if "program name"
@@ -345,8 +345,8 @@ a procedure."
(assemble-program
'((begin-program foo ((name . foo)))
(begin-standard-arity () 2 #f)
- (load-constant 1 42)
- (return 1)
+ (load-constant 0 42)
+ (return 0)
(end-arity)
(end-program))))))
@@ -356,8 +356,8 @@ a procedure."
(assemble-program
'((begin-program foo ((name . foo)))
(begin-standard-arity () 2 #f)
- (load-constant 1 42)
- (return 1)
+ (load-constant 0 42)
+ (return 0)
(end-arity)
(end-program)))))
(pass-if-equal "#"
From 4afb46f8599854dd36af25576ec4b7ab1c39745f Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 21 Oct 2015 15:02:08 +0200
Subject: [PATCH 061/865] Minor assembler cleanups
* module/system/vm/assembler.scm (shuffling-assembler): Minor renames.
---
module/system/vm/assembler.scm | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/module/system/vm/assembler.scm b/module/system/vm/assembler.scm
index bad298d0d..d50ab13b5 100644
--- a/module/system/vm/assembler.scm
+++ b/module/system/vm/assembler.scm
@@ -691,13 +691,13 @@ later by the linker."
(emit asm 1 0 c)
(emit-drop asm 2)))))
(('X8_S12_S12 '<- 'X8_C24)
- #'(lambda (asm dst a c)
+ #'(lambda (asm dst a const)
(cond
((< (logior dst a) (ash 1 12))
- (emit asm dst a c))
+ (emit asm dst a const))
(else
(emit-push asm a)
- (emit asm 0 0 c)
+ (emit asm 0 0 const)
(emit-pop asm dst)))))
(('X8_S12_C12 '<-)
From f03960412ebf3ac40686a79b8ed5ebf7c6e49b18 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Wed, 21 Oct 2015 15:02:28 +0200
Subject: [PATCH 062/865] Add stack size computation to disassembler
* module/system/vm/disassembler.scm (define-stack-effect-parser)
(stack-effect-parsers, instruction-stack-size-after): New stack size
facility.
(define-clobber-parser, clobber-parsers, instruction-slot-clobbers):
Take incoming and outgoing stack sizes as arguments to interpret
SP-relative clobbers.
* module/system/vm/frame.scm (compute-frame-sizes): New helper that
computes frame sizes for each position in a function.
(compute-killv): Adapt to compute the clobbered set given the computed
frame sizes.
---
module/system/vm/disassembler.scm | 78 +++++++++++++++++++++++++++----
module/system/vm/frame.scm | 54 +++++++++++++++++++--
2 files changed, 121 insertions(+), 11 deletions(-)
diff --git a/module/system/vm/disassembler.scm b/module/system/vm/disassembler.scm
index c1a8ce700..b76433ba5 100644
--- a/module/system/vm/disassembler.scm
+++ b/module/system/vm/disassembler.scm
@@ -39,6 +39,7 @@
instruction-length
instruction-has-fallthrough?
instruction-relative-jump-targets
+ instruction-stack-size-after
instruction-slot-clobbers))
(define-syntax-rule (u32-ref buf n)
@@ -536,15 +537,70 @@ address of that offset."
(let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
((vector-ref jump-parsers opcode) code pos)))
+(define-syntax define-stack-effect-parser
+ (lambda (x)
+ (define (stack-effect-parser name)
+ (case name
+ ((push)
+ #'(lambda (code pos size) (+ size 1)))
+ ((pop)
+ #'(lambda (code pos size) (- size 1)))
+ ((drop)
+ #'(lambda (code pos size)
+ (let ((count (ash (bytevector-u32-native-ref code pos) -8)))
+ (- size count))))
+ ((alloc-frame reset-frame)
+ #'(lambda (code pos size)
+ (let ((nlocals (ash (bytevector-u32-native-ref code pos) -8)))
+ nlocals)))
+ ((receive)
+ #'(lambda (code pos size)
+ (let ((nlocals (ash (bytevector-u32-native-ref code (+ pos 4))
+ -8)))
+ nlocals)))
+ ((bind-kwargs)
+ #'(lambda (code pos size)
+ (let ((ntotal (ash (bytevector-u32-native-ref code (+ pos 8)) -8)))
+ ntotal)))
+ ((bind-rest)
+ #'(lambda (code pos size)
+ (let ((dst (ash (bytevector-u32-native-ref code pos) -8)))
+ (+ dst 1))))
+ ((assert-nargs-ee/locals)
+ #'(lambda (code pos size)
+ (let ((nargs (logand (ash (bytevector-u32-native-ref code pos) -8)
+ #xfff))
+ (nlocals (ash (bytevector-u32-native-ref code pos) -20)))
+ (+ nargs nlocals))))
+ ((call call-label)
+ #'(lambda (code pos size) #f))
+ ((tail-call tail-call-label tail-call/shuffle tail-apply)
+ #'(lambda (code pos size) #f))
+ (else
+ #f)))
+ (syntax-case x ()
+ ((_ name opcode kind word0 word* ...)
+ (let ((parser (stack-effect-parser (syntax->datum #'name))))
+ (if parser
+ #`(vector-set! stack-effect-parsers opcode #,parser)
+ #'(begin)))))))
+
+(define stack-effect-parsers (make-vector 256 (lambda (code pos size) size)))
+(visit-opcodes define-stack-effect-parser)
+
+(define (instruction-stack-size-after code pos size)
+ (let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
+ ((vector-ref stack-effect-parsers opcode) code pos size)))
+
(define-syntax define-clobber-parser
(lambda (x)
(syntax-case x ()
- ((_ name opcode kind arg ...)
+ ((_ name opcode kind arg0 arg* ...)
(case (syntax->datum #'kind)
((!)
(case (syntax->datum #'name)
((call call-label)
- #'(let ((parse (lambda (code pos nslots)
+ #'(let ((parse (lambda (code pos nslots-in nslots-out)
(call-with-values
(lambda ()
(disassemble-one code (/ pos 4)))
@@ -552,26 +608,32 @@ address of that offset."
(match elt
((_ proc . _)
(let lp ((slot (- proc 2)))
- (if (< slot nslots)
+ (if (< slot nslots-in)
(cons slot (lp (1+ slot)))
'())))))))))
(vector-set! clobber-parsers opcode parse)))
(else
#'(begin))))
((<-)
- #'(let ((parse (lambda (code pos nslots)
+ #`(let ((parse (lambda (code pos nslots-in nslots-out)
(call-with-values
(lambda ()
(disassemble-one code (/ pos 4)))
(lambda (len elt)
(match elt
- ((_ dst . _) (list dst))))))))
+ ((_ dst . _)
+ #,(case (syntax->datum #'arg0)
+ ((X8_F24 X8_F12_F12)
+ #'(list dst))
+ (else
+ #'(list (- nslots-out 1 dst)))))))))))
(vector-set! clobber-parsers opcode parse)))
(else (error "unexpected instruction kind" #'kind)))))))
-(define clobber-parsers (make-vector 256 (lambda (code pos nslots) '())))
+(define clobber-parsers
+ (make-vector 256 (lambda (code pos nslots-in nslots-out) '())))
(visit-opcodes define-clobber-parser)
-(define (instruction-slot-clobbers code pos nslots)
+(define (instruction-slot-clobbers code pos nslots-in nslots-out)
(let ((opcode (logand (bytevector-u32-native-ref code pos) #xff)))
- ((vector-ref clobber-parsers opcode) code pos nslots)))
+ ((vector-ref clobber-parsers opcode) code pos nslots-in nslots-out)))
diff --git a/module/system/vm/frame.scm b/module/system/vm/frame.scm
index b84f6683e..7f0211da8 100644
--- a/module/system/vm/frame.scm
+++ b/module/system/vm/frame.scm
@@ -25,6 +25,7 @@
#:use-module (system vm debug)
#:use-module (system vm disassembler)
#:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-11)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 match)
#:export (binding-index
@@ -83,6 +84,49 @@
(lp (1+ n) (+ pos (vector-ref parsed n)))))
preds))
+(define (compute-frame-sizes code parsed initial-size)
+ (let ((in-sizes (make-vector (vector-length parsed) #f))
+ (out-sizes (make-vector (vector-length parsed) #f)))
+ ;; This only computes all possible valid stack sizes if the bytecode
+ ;; is sorted topologically. Guiles' compiler does this currently,
+ ;; but if that changes we should do a proper pre-order visit. Of
+ ;; course the bytecode has to be valid too.
+ (define (find-idx n diff)
+ (let lp ((n n) (diff diff))
+ (cond
+ ((negative? diff)
+ (lp (1- n) (+ diff (vector-ref parsed (1- n)))))
+ ((positive? diff)
+ (lp (1+ n) (- diff (vector-ref parsed n))))
+ (else n))))
+ (vector-set! in-sizes 0 initial-size)
+ (let lp ((n 0) (pos 0))
+ (define (offset->idx target)
+ (call-with-values (lambda ()
+ (if (>= target pos)
+ (values n pos)
+ (values 0 0)))
+ (lambda (n pos)
+ (let lp ((n n) (pos pos))
+ (cond
+ ((= pos target) n)
+ ((< pos target) (lp (1+ n) (+ pos (vector-ref parsed n))))
+ (else (error "bad target" target)))))))
+ (when (< n (vector-length parsed))
+ (let* ((in (vector-ref in-sizes n))
+ (out (instruction-stack-size-after code pos in)))
+ (vector-set! out-sizes n out)
+ (when out
+ (when (instruction-has-fallthrough? code pos)
+ (vector-set! in-sizes (1+ n) out))
+ (for-each (lambda (target)
+ (let ((idx (find-idx n target)))
+ (when idx
+ (vector-set! in-sizes idx out))))
+ (instruction-relative-jump-targets code pos))))
+ (lp (1+ n) (+ pos (vector-ref parsed n)))))
+ (values in-sizes out-sizes)))
+
(define (compute-genv parsed defs)
(let ((genv (make-vector (vector-length parsed) '())))
(define (add-def! pos var)
@@ -118,8 +162,11 @@
by-slot))
(define (compute-killv code parsed defs)
- (let ((defs-by-slot (compute-defs-by-slot defs))
- (killv (make-vector (vector-length parsed) #f)))
+ (let*-values (((defs-by-slot) (compute-defs-by-slot defs))
+ ((initial-frame-size) (vector-length defs-by-slot))
+ ((in-sizes out-sizes)
+ (compute-frame-sizes code parsed initial-frame-size))
+ ((killv) (make-vector (vector-length parsed) #f)))
(define (kill-slot! n slot)
(bit-set*! (vector-ref killv n) (vector-ref defs-by-slot slot) #t))
(let lp ((n 0))
@@ -147,7 +194,8 @@
(when (< slot (vector-length defs-by-slot))
(kill-slot! n slot)))
(instruction-slot-clobbers code pos
- (vector-length defs-by-slot)))
+ (vector-ref in-sizes n)
+ (vector-ref out-sizes n)))
(lp (1+ n) (+ pos (vector-ref parsed n)))))
killv))
From 467e587d68fac4a4c269e892740cd5077f5da815 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 11:02:18 +0000
Subject: [PATCH 063/865] Update VM documentation for new stack layout
* doc/ref/vm.texi: Update for new stack layout.
* module/system/vm/disassembler.scm (code-annotation): Print the frame
sizes after alloc-frame, reset-frame, etc to make reading the
disassembly easier.
---
doc/ref/vm.texi | 437 +++++++++++++++++-------------
module/system/vm/disassembler.scm | 8 +-
2 files changed, 256 insertions(+), 189 deletions(-)
diff --git a/doc/ref/vm.texi b/doc/ref/vm.texi
index ba31d7ccb..45c3928a0 100644
--- a/doc/ref/vm.texi
+++ b/doc/ref/vm.texi
@@ -144,19 +144,23 @@ course is the tail call case, @pxref{Tail Calls}.)
The structure of the top stack frame is as follows:
@example
- /------------------\ <- top of stack
- | Local N-1 | <- sp
| ... |
- | Local 1 |
- | Local 0 | <- fp = SCM_FRAME_LOCALS_ADDRESS (fp)
- +==================+
+ +==================+ <- fp + 2 = SCM_FRAME_PREVIOUS_SP (fp)
+ | Dynamic link |
+ +------------------+
| Return address |
- | Dynamic link | <- fp - 2 = SCM_FRAME_LOWER_ADDRESS (fp)
- +==================+
- | | <- fp - 3 = SCM_FRAME_PREVIOUS_SP (fp)
+ +==================+ <- fp
+ | Local 0 |
+ +------------------+
+ | Local 1 |
+ +------------------+
+ | ... |
+ +------------------+
+ | Local N-1 |
+ \------------------/ <- sp
@end example
-In the above drawing, the stack grows upward. Usually the procedure
+In the above drawing, the stack grows downward. Usually the procedure
being applied is in local 0, followed by the arguments from local 1.
After that are enough slots to store the various lexically-bound and
temporary values that are needed in the function's application.
@@ -164,7 +168,8 @@ temporary values that are needed in the function's application.
The @dfn{return address} is the @code{ip} that was in effect before this
program was applied. When we return from this activation frame, we will
jump back to this @code{ip}. Likewise, the @dfn{dynamic link} is the
-@code{fp} in effect before this program was applied.
+offset of the @code{fp} that was in effect before this program was
+applied, relative to the current @code{fp}.
To prepare for a non-tail application, Guile's VM will emit code that
shuffles the function to apply and its arguments into appropriate stack
@@ -176,6 +181,12 @@ new call frame.
In this way, the dynamic link links the current frame to the previous
frame. Computing a stack trace involves traversing these frames.
+As an implementation detail, we actually store the dynamic link as an
+offset and not an absolute value because the stack can move at runtime
+as it expands or during partial continuation calls. If it were an
+absolute value, we would have to walk the frames, relocating frame
+pointers.
+
@node Variables and the VM
@subsection Variables and the VM
@@ -263,54 +274,71 @@ We can see how these concepts tie together by disassembling the
@smallexample
scheme@@(guile-user)> (define (foo a) (lambda (b) (list foo a b)))
scheme@@(guile-user)> ,x foo
-Disassembly of # at #x203be34:
+Disassembly of # at #xddb824:
- 0 (assert-nargs-ee/locals 2 1) ;; 1 arg, 1 local at (unknown file):1:0
- 1 (make-closure 2 6 1) ;; anonymous procedure at #x203be50 (1 free var)
- 4 (free-set! 2 1 0) ;; free var 0
- 6 (return 2)
+ 0 (assert-nargs-ee/locals 2 0) ;; 2 slots (1 arg) at (unknown file):1:0
+ 1 (make-closure 1 6 1) ;; anonymous procedure at #xddb840 (1 free var)
+ 4 (free-set! 1 0 0) ;; free var 0
+ 6 (return 1)
----------------------------------------
-Disassembly of anonymous procedure at #x203be50:
+Disassembly of anonymous procedure at #xddb840:
- 0 (assert-nargs-ee/locals 2 3) ;; 1 arg, 3 locals at (unknown file):1:0
- 1 (toplevel-box 2 73 57 71 #t) ;; `foo'
- 6 (box-ref 2 2)
- 7 (make-short-immediate 3 772) ;; ()
- 8 (cons 3 1 3)
- 9 (free-ref 4 0 0) ;; free var 0
- 11 (cons 3 4 3)
- 12 (cons 2 2 3)
- 13 (return 2)
+ 0 (assert-nargs-ee/locals 2 2) ;; 4 slots (1 arg) at (unknown file):1:16
+ 1 (toplevel-box 1 73 57 67 #t) ;; `foo'
+ 6 (box-ref 1 1)
+ 7 (make-short-immediate 0 772) ;; () at (unknown file):1:28
+ 8 (cons 2 2 0)
+ 9 (free-ref 3 3 0) ;; free var 0
+ 11 (cons 3 3 2)
+ 12 (cons 3 1 3)
+ 13 (return 3)
@end smallexample
First there's some prelude, where @code{foo} checks that it was called
with only 1 argument. Then at @code{ip} 1, we allocate a new closure
-and store it in slot 2. The `6' in the @code{(make-closure 2 6 1)} is a
-relative offset from the instruction pointer of the code for the
-closure.
+and store it in slot 1, relative to the @code{sp}.
-A closure is code with data. We already have the code part initialized;
-what remains is to set the data. @code{Ip} 4 initializes free variable
-0 in the new closure with the value from local variable 1, which
-corresponds to the first argument of @code{foo}: `a'. Finally we return
-the closure.
+At run-time, local variables in Guile are usually addressed relative to
+the stack pointer, which leads to a pleasantly efficient
+@code{sp[@var{n}]} access. However it can make the disassembly hard to
+read, because the @code{sp} can change during the function, and because
+incoming arguments are relative to the @code{fp}, not the @code{sp}.
+
+To know what @code{fp}-relative slot corresponds to an
+@code{sp}-relative reference, scan up in the disassembly until you get
+to a ``@var{n} slots'' annotation; in our case, 2, indicating that the
+frame has space for 2 slots. Thus a zero-indexed @code{sp}-relative
+slot of 1 corresponds to the @code{fp}-relative slot of 0, which
+initially held the value of the closure being called. This means that
+Guile doesn't need the value of the closure to compute its result, and
+so slot 0 was free for re-use, in this case for the result of making a
+new closure.
+
+A closure is code with data. The @code{6} in the @code{(make-closure 1
+6 1)} is a relative offset from the instruction pointer of the code for
+the closure, and the final @code{1} indicates that the closure has space
+for 1 free variable. @code{Ip} 4 initializes free variable 0 in the new
+closure with the value from @code{sp}-relative slot 0, which corresponds
+to @code{fp}-relative slot 1, the first argument of @code{foo}:
+@code{a}. Finally we return the closure.
The second stanza disassembles the code for the closure. After the
prelude, we load the variable for the toplevel variable @code{foo} into
-local variable 2. This lookup occurs lazily, the first time the
-variable is actually referenced, and the location of the lookup is
-cached so that future references are very cheap. @xref{Top-Level
-Environment Instructions}, for more details. The @code{box-ref}
-dereferences the variable cell, replacing the contents of local 2.
+slot 1. This lookup occurs lazily, the first time the variable is
+actually referenced, and the location of the lookup is cached so that
+future references are very cheap. @xref{Top-Level Environment
+Instructions}, for more details. The @code{box-ref} dereferences the
+variable cell, replacing the contents of slot 1.
What follows is a sequence of conses to build up the result list.
@code{Ip} 7 makes the tail of the list. @code{Ip} 8 conses on the value
-in local 1, corresponding to the first argument to the closure: `b'.
-@code{Ip} 9 loads free variable 0 of local 0 -- the procedure being
-called -- into slot 4, then @code{ip} 11 conses it onto the list.
-Finally we cons local 2, containing the @code{foo} toplevel, onto the
-front of the list, and we return it.
+in slot 2, corresponding to the first argument to the closure: @code{b}.
+@code{Ip} 9 loads free variable 0 of slot 3 -- the procedure being
+called, in @code{fp}-relative slot 0 -- into slot 3, then @code{ip} 11
+conses it onto the list. Finally we cons the value in slot 1,
+containing the @code{foo} toplevel, onto the front of the list, and we
+return it.
@node Object File Format
@@ -431,10 +459,16 @@ instruction describe the operands. There are a number of different ways
operands can be encoded.
@table @code
-@item u@var{n}
-An unsigned @var{n}-bit integer. Usually indicates the index of a local
-variable, but some instructions interpret these operands as immediate
-values.
+@item s@var{n}
+An unsigned @var{n}-bit integer, indicating the @code{sp}-relative index
+of a local variable.
+@item f@var{n}
+An unsigned @var{n}-bit integer, indicating the @code{fp}-relative index
+of a local variable. Used when a continuation accepts a variable number
+of values, to shuffle received values into known locations in the
+frame.
+@item c@var{n}
+An unsigned @var{n}-bit integer, indicating a constant value.
@item l24
An offset from the current @code{ip}, in 32-bit units, as a signed
24-bit value. Indicates a bytecode address, for a relative jump.
@@ -452,7 +486,7 @@ and indicate the high and low bits, respectively. Normally only used on
A statically allocated non-immediate. The address of the non-immediate
is encoded as a signed 32-bit integer, and indicates a relative offset
in 32-bit units. Think of it as @code{SCM x = ip + offset}.
-@item s32
+@item r32
Indirect scheme value, like @code{n32} but indirected. Think of it as
@code{SCM *x = ip + offset}.
@item l32
@@ -478,7 +512,7 @@ operands occupying the lower bits.
For example, consider the following instruction specification:
-@deftypefn Instruction {} free-set! u12:@var{dst} u12:@var{src} x8:@var{_} u24:@var{idx}
+@deftypefn Instruction {} free-set! s12:@var{dst} s12:@var{src} x8:@var{_} c24:@var{idx}
Set free variable @var{idx} from the closure @var{dst} to @var{src}.
@end deftypefn
@@ -504,11 +538,6 @@ In addition, some Scheme primitives have their own inline
implementations. For example, in the previous section we saw
@code{cons}.
-Guile's instruction set is a @emph{complete} instruction set, in that it
-provides the instructions that are suited to the problem, and is not
-concerned with making a minimal, orthogonal set of instructions. More
-instructions may be added over time.
-
@menu
* Lexical Environment Instructions::
* Top-Level Environment Instructions::
@@ -532,8 +561,8 @@ These instructions access and mutate the lexical environment of a
compiled procedure---its free and bound variables. @xref{Stack Layout},
for more information on the format of stack frames.
-@deftypefn Instruction {} mov u12:@var{dst} u12:@var{src}
-@deftypefnx Instruction {} long-mov u24:@var{dst} x8:@var{_} u24:@var{src}
+@deftypefn Instruction {} mov s12:@var{dst} s12:@var{src}
+@deftypefnx Instruction {} long-mov s24:@var{dst} x8:@var{_} s24:@var{src}
Copy a value from one local slot to another.
As discussed previously, procedure arguments and local variables are
@@ -543,7 +572,13 @@ instructions redundant. However there are some cases in which shuffling
is necessary, and in those cases, @code{mov} is the thing to use.
@end deftypefn
-@deftypefn Instruction {} make-closure u24:@var{dst} l32:@var{offset} x8:@var{_} u24:@var{nfree}
+@deftypefn Instruction {} long-fmov f24:@var{dst} x8:@var{_} f24:@var{src}
+Copy a value from one local slot to another, but addressing slots
+relative to the @code{fp} instead of the @code{sp}. This is used when
+shuffling values into place after multiple-value returns.
+@end deftypefn
+
+@deftypefn Instruction {} make-closure s24:@var{dst} l32:@var{offset} x8:@var{_} c24:@var{nfree}
Make a new closure, and write it to @var{dst}. The code for the closure
will be found at @var{offset} words from the current @code{ip}.
@var{offset} is a signed 32-bit integer. Space for @var{nfree} free
@@ -553,12 +588,12 @@ The size of a closure is currently two words, plus one word per free
variable.
@end deftypefn
-@deftypefn Instruction {} free-ref u12:@var{dst} u12:@var{src} x8:@var{_} u24:@var{idx}
+@deftypefn Instruction {} free-ref s12:@var{dst} s12:@var{src} x8:@var{_} c24:@var{idx}
Load free variable @var{idx} from the closure @var{src} into local slot
@var{dst}.
@end deftypefn
-@deftypefn Instruction {} free-set! u12:@var{dst} u12:@var{src} x8:@var{_} u24:@var{idx}
+@deftypefn Instruction {} free-set! s12:@var{dst} s12:@var{src} x8:@var{_} c24:@var{idx}
Set free variable @var{idx} from the closure @var{dst} to @var{src}.
This instruction is usually used when initializing a closure's free
@@ -572,16 +607,16 @@ their value at one point in time. Variables are also used in the
implementation of top-level bindings; see the next section for more
information.
-@deftypefn Instruction {} box u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} box s12:@var{dst} s12:@var{src}
Create a new variable holding @var{src}, and place it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} box-ref u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} box-ref s12:@var{dst} s12:@var{src}
Unpack the variable at @var{src} into @var{dst}, asserting that the
variable is actually bound.
@end deftypefn
-@deftypefn Instruction {} box-set! u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} box-set! s12:@var{dst} s12:@var{src}
Set the contents of the variable at @var{dst} to @var{set}.
@end deftypefn
@@ -597,23 +632,23 @@ The location in which a toplevel binding is stored can be looked up once
and cached for later. The binding itself may change over time, but its
location will stay constant.
-@deftypefn Instruction {} current-module u24:@var{dst}
+@deftypefn Instruction {} current-module s24:@var{dst}
Store the current module in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} resolve u24:@var{dst} b1:@var{bound?} x7:@var{_} u24:@var{sym}
+@deftypefn Instruction {} resolve s24:@var{dst} b1:@var{bound?} x7:@var{_} s24:@var{sym}
Resolve @var{sym} in the current module, and place the resulting
variable in @var{dst}. An error will be signalled if no variable is
found. If @var{bound?} is true, an error will be signalled if the
variable is unbound.
@end deftypefn
-@deftypefn Instruction {} define! u12:@var{sym} u12:@var{val}
+@deftypefn Instruction {} define! s12:@var{sym} s12:@var{val}
Look up a binding for @var{sym} in the current module, creating it if
necessary. Set its value to @var{val}.
@end deftypefn
-@deftypefn Instruction {} toplevel-box u24:@var{dst} s32:@var{var-offset} s32:@var{mod-offset} n32:@var{sym-offset} b1:@var{bound?} x31:@var{_}
+@deftypefn Instruction {} toplevel-box s24:@var{dst} r32:@var{var-offset} r32:@var{mod-offset} n32:@var{sym-offset} b1:@var{bound?} x31:@var{_}
Load a value. The value will be fetched from memory, @var{var-offset}
32-bit words away from the current instruction pointer.
@var{var-offset} is a signed value. Up to here, @code{toplevel-box} is
@@ -633,7 +668,7 @@ cache next time. If @var{bound?} is true, an error will be signalled if
the variable is unbound.
@end deftypefn
-@deftypefn Instruction {} module-box u24:@var{dst} s32:@var{var-offset} n32:@var{mod-offset} n32:@var{sym-offset} b1:@var{bound?} x31:@var{_}
+@deftypefn Instruction {} module-box s24:@var{dst} r32:@var{var-offset} n32:@var{mod-offset} n32:@var{sym-offset} b1:@var{bound?} x31:@var{_}
Like @code{toplevel-box}, except @var{mod-offset} points at a module
identifier instead of the module itself. A module identifier is a
module name, as a list, prefixed by a boolean. If the prefix is true,
@@ -651,23 +686,25 @@ is that arguments are passed and values returned on the stack.
For calls, both in tail position and in non-tail position, we require
that the procedure and the arguments already be shuffled into place
befor the call instruction. ``Into place'' for a tail call means that
-the procedure should be in slot 0, and the arguments should follow. For
-a non-tail call, if the procedure is in slot @var{n}, the arguments
-should follow from slot @var{n}+1, and there should be two free slots at
-@var{n}-1 and @var{n}-2 in which to save the @code{ip} and @code{fp}.
+the procedure should be in slot 0, relative to the @code{fp}, and the
+arguments should follow. For a non-tail call, if the procedure is in
+@code{fp}-relative slot @var{n}, the arguments should follow from slot
+@var{n}+1, and there should be two free slots at @var{n}-1 and @var{n}-2
+in which to save the @code{ip} and @code{fp}.
Returning values is similar. Multiple-value returns should have values
-already shuffled down to start from slot 1 before emitting
-@code{return-values}. There is a short-cut in the single-value case, in
-that @code{return} handles the trivial shuffling itself. We start from
-slot 1 instead of slot 0 to make tail calls to @code{values} trivial.
+already shuffled down to start from @code{fp}-relative slot 1 before
+emitting @code{return-values}. There is a short-cut in the single-value
+case, in that @code{return} handles the trivial shuffling itself. We
+start from slot 1 instead of slot 0 to make tail calls to @code{values}
+trivial.
In both calls and returns, the @code{sp} is used to indicate to the
callee or caller the number of arguments or return values, respectively.
After receiving return values, it is the caller's responsibility to
@dfn{restore the frame} by resetting the @code{sp} to its former value.
-@deftypefn Instruction {} call u24:@var{proc} x8:@var{_} u24:@var{nlocals}
+@deftypefn Instruction {} call f24:@var{proc} x8:@var{_} c24:@var{nlocals}
Call a procedure. @var{proc} is the local corresponding to a procedure.
The two values below @var{proc} will be overwritten by the saved call
frame data. The new frame will have space for @var{nlocals} locals: one
@@ -680,7 +717,7 @@ number can be had by subtracting the address of @var{proc} from the
post-call @code{sp}.
@end deftypefn
-@deftypefn Instruction {} call-label u24:@var{proc} x8:@var{_} u24:@var{nlocals} l32:@var{label}
+@deftypefn Instruction {} call-label f24:@var{proc} x8:@var{_} c24:@var{nlocals} l32:@var{label}
Call a procedure in the same compilation unit.
This instruction is just like @code{call}, except that instead of
@@ -690,31 +727,31 @@ the current @code{ip}. Since @var{proc} is not dereferenced, it may be
some other representation of the closure.
@end deftypefn
-@deftypefn Instruction {} tail-call u24:@var{nlocals}
+@deftypefn Instruction {} tail-call c24:@var{nlocals}
Tail-call a procedure. Requires that the procedure and all of the
arguments have already been shuffled into position. Will reset the
frame to @var{nlocals}.
@end deftypefn
-@deftypefn Instruction {} tail-call-label u24:@var{nlocals} l32:@var{label}
+@deftypefn Instruction {} tail-call-label c24:@var{nlocals} l32:@var{label}
Tail-call a known procedure. As @code{call} is to @code{call-label},
@code{tail-call} is to @code{tail-call-label}.
@end deftypefn
-@deftypefn Instruction {} tail-call/shuffle u24:@var{from}
+@deftypefn Instruction {} tail-call/shuffle f24:@var{from}
Tail-call a procedure. The procedure should already be set to slot 0.
The rest of the args are taken from the frame, starting at @var{from},
shuffled down to start at slot 0. This is part of the implementation of
the @code{call-with-values} builtin.
@end deftypefn
-@deftypefn Instruction {} receive u12:@var{dst} u12:@var{proc} x8:@var{_} u24:@var{nlocals}
+@deftypefn Instruction {} receive f12:@var{dst} f12:@var{proc} x8:@var{_} c24:@var{nlocals}
Receive a single return value from a call whose procedure was in
@var{proc}, asserting that the call actually returned at least one
value. Afterwards, resets the frame to @var{nlocals} locals.
@end deftypefn
-@deftypefn Instruction {} receive-values u24:@var{proc} b1:@var{allow-extra?} x7:@var{_} u24:@var{nvalues}
+@deftypefn Instruction {} receive-values f24:@var{proc} b1:@var{allow-extra?} x7:@var{_} c24:@var{nvalues}
Receive a return of multiple values from a call whose procedure was in
@var{proc}. If fewer than @var{nvalues} values were returned, signal an
error. Unless @var{allow-extra?} is true, require that the number of
@@ -722,7 +759,7 @@ return values equals @var{nvalues} exactly. After @code{receive-values}
has run, the values can be copied down via @code{mov}, or used in place.
@end deftypefn
-@deftypefn Instruction {} return u24:@var{src}
+@deftypefn Instruction {} return s24:@var{src}
Return a value.
@end deftypefn
@@ -755,21 +792,21 @@ cost of parsing keyword arguments. (At the time of this writing, calling
procedures with keyword arguments is typically two to four times as
costly as calling procedures with a fixed set of arguments.)
-@deftypefn Instruction {} assert-nargs-ee u24:@var{expected}
-@deftypefnx Instruction {} assert-nargs-ge u24:@var{expected}
-@deftypefnx Instruction {} assert-nargs-le u24:@var{expected}
+@deftypefn Instruction {} assert-nargs-ee c24:@var{expected}
+@deftypefnx Instruction {} assert-nargs-ge c24:@var{expected}
+@deftypefnx Instruction {} assert-nargs-le c24:@var{expected}
If the number of actual arguments is not @code{==}, @code{>=}, or
@code{<=} @var{expected}, respectively, signal an error.
-The number of arguments is determined by subtracting the frame pointer
-from the stack pointer (@code{sp + 1 - fp}). @xref{Stack Layout}, for
-more details on stack frames. Note that @var{expected} includes the
+The number of arguments is determined by subtracting the stack pointer
+from the frame pointer (@code{fp - sp}). @xref{Stack Layout}, for more
+details on stack frames. Note that @var{expected} includes the
procedure itself.
@end deftypefn
-@deftypefn Instruction {} br-if-nargs-ne u24:@var{expected} x8:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-nargs-lt u24:@var{expected} x8:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-nargs-gt u24:@var{expected} x8:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-nargs-ne c24:@var{expected} x8:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-nargs-lt c24:@var{expected} x8:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-nargs-gt c24:@var{expected} x8:@var{_} l24:@var{offset}
If the number of actual arguments is not equal, less than, or greater
than @var{expected}, respectively, add @var{offset}, a signed 24-bit
number, to the current instruction pointer. Note that @var{expected}
@@ -779,26 +816,26 @@ These instructions are used to implement multiple arities, as in
@code{case-lambda}. @xref{Case-lambda}, for more information.
@end deftypefn
-@deftypefn Instruction {} alloc-frame u24:@var{nlocals}
+@deftypefn Instruction {} alloc-frame c24:@var{nlocals}
Ensure that there is space on the stack for @var{nlocals} local
variables, setting them all to @code{SCM_UNDEFINED}, except those values
that are already on the stack.
@end deftypefn
-@deftypefn Instruction {} reset-frame u24:@var{nlocals}
+@deftypefn Instruction {} reset-frame c24:@var{nlocals}
Like @code{alloc-frame}, but doesn't check that the stack is big enough,
and doesn't initialize values to @code{SCM_UNDEFINED}. Used to reset
the frame size to something less than the size that was previously set
via alloc-frame.
@end deftypefn
-@deftypefn Instruction {} assert-nargs-ee/locals u12:@var{expected} u12:@var{nlocals}
+@deftypefn Instruction {} assert-nargs-ee/locals c12:@var{expected} c12:@var{nlocals}
Equivalent to a sequence of @code{assert-nargs-ee} and
@code{reserve-locals}. The number of locals reserved is @var{expected}
+ @var{nlocals}.
@end deftypefn
-@deftypefn Instruction {} br-if-npos-gt u24:@var{nreq} x8:@var{_} u24:@var{npos} x8:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-npos-gt c24:@var{nreq} x8:@var{_} c24:@var{npos} x8:@var{_} l24:@var{offset}
Find the first positional argument after @var{nreq}. If it is greater
than @var{npos}, jump to @var{offset}.
@@ -808,7 +845,7 @@ and an earlier clause has keywords and no rest arguments.
clause to apply.
@end deftypefn
-@deftypefn Instruction {} bind-kwargs u24:@var{nreq} u8:@var{flags} u24:@var{nreq-and-opt} x8:@var{_} u24:@var{ntotal} n32:@var{kw-offset}
+@deftypefn Instruction {} bind-kwargs c24:@var{nreq} c8:@var{flags} c24:@var{nreq-and-opt} x8:@var{_} c24:@var{ntotal} n32:@var{kw-offset}
@var{flags} is a bitfield, whose lowest bit is @var{allow-other-keys},
second bit is @var{has-rest}, and whose following six bits are unused.
@@ -829,7 +866,7 @@ will signal an error if an unknown key is found.
A macro-mega-instruction.
@end deftypefn
-@deftypefn Instruction {} bind-rest u24:@var{dst}
+@deftypefn Instruction {} bind-rest f24:@var{dst}
Collect any arguments at or above @var{dst} into a list, and store that
list at @var{dst}.
@end deftypefn
@@ -851,25 +888,25 @@ compiler probably shouldn't emit code with these instructions. However,
it's still interesting to know how these things work, so we document
these trampoline instructions here.
-@deftypefn Instruction {} subr-call u24:@var{ptr-idx}
+@deftypefn Instruction {} subr-call c24:@var{ptr-idx}
Call a subr, passing all locals in this frame as arguments. Fetch the
foreign pointer from @var{ptr-idx}, a free variable. Return from the
calling frame.
@end deftypefn
-@deftypefn Instruction {} foreign-call u12:@var{cif-idx} u12:@var{ptr-idx}
+@deftypefn Instruction {} foreign-call c12:@var{cif-idx} c12:@var{ptr-idx}
Call a foreign function. Fetch the @var{cif} and foreign pointer from
@var{cif-idx} and @var{ptr-idx}, both free variables. Return from the calling
frame. Arguments are taken from the stack.
@end deftypefn
-@deftypefn Instruction {} continuation-call u24:@var{contregs}
+@deftypefn Instruction {} continuation-call c24:@var{contregs}
Return to a continuation, nonlocally. The arguments to the continuation
are taken from the stack. @var{contregs} is a free variable containing
the reified continuation.
@end deftypefn
-@deftypefn Instruction {} compose-continuation u24:@var{cont}
+@deftypefn Instruction {} compose-continuation c24:@var{cont}
Compose a partial continution with the current continuation. The
arguments to the continuation are taken from the stack. @var{cont} is a
free variable containing the reified continuation.
@@ -881,7 +918,7 @@ This instruction is part of the implementation of @code{apply}, and is
not generated by the compiler.
@end deftypefn
-@deftypefn Instruction {} builtin-ref u12:@var{dst} u12:@var{idx}
+@deftypefn Instruction {} builtin-ref s12:@var{dst} c12:@var{idx}
Load a builtin stub by index into @var{dst}.
@end deftypefn
@@ -901,60 +938,60 @@ All the conditional branch instructions described below have an
@var{invert} parameter, which if true reverses the test:
@code{br-if-true} becomes @code{br-if-false}, and so on.
-@deftypefn Instruction {} br-if-true u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-true s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is true for the purposes of Scheme, add
@var{offset} to the current instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-null u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-null s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is the end-of-list or Lisp nil, add
@var{offset} to the current instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-nil u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-nil s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is false to Lisp, add @var{offset} to the
current instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-pair u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-pair s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is a pair, add @var{offset} to the current
instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-struct u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-struct s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is a struct, add @var{offset} number to the
current instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-char u24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-char s24:@var{test} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{test} is a char, add @var{offset} to the current
instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-tc7 u24:@var{test} b1:@var{invert} u7:@var{tc7} l24:@var{offset}
+@deftypefn Instruction {} br-if-tc7 s24:@var{test} b1:@var{invert} u7:@var{tc7} l24:@var{offset}
If the value in @var{test} has the TC7 given in the second word, add
@var{offset} to the current instruction pointer. TC7 codes are part of
the way Guile represents non-immediate objects, and are deep wizardry.
See @code{libguile/tags.h} for all the details.
@end deftypefn
-@deftypefn Instruction {} br-if-eq u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-eqv u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-equal u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-eq s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-eqv s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-equal s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{a} is @code{eq?}, @code{eqv?}, or @code{equal?} to
the value in @var{b}, respectively, add @var{offset} to the current
instruction pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-= u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-< u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
-@deftypefnx Instruction {} br-if-<= u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-< s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefnx Instruction {} br-if-<= s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the value in @var{a} is @code{=}, @code{<}, or @code{<=} to the value
in @var{b}, respectively, add @var{offset} to the current instruction
pointer.
@end deftypefn
-@deftypefn Instruction {} br-if-logtest u12:@var{a} u12:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
+@deftypefn Instruction {} br-if-logtest s24:@var{a} x8:@var{_} s24:@var{b} b1:@var{invert} x7:@var{_} l24:@var{offset}
If the bitwise intersection of the integers in @var{a} and @var{b} is
nonzero, add @var{offset} to the current instruction pointer.
@end deftypefn
@@ -969,17 +1006,17 @@ two kinds.
The first set of instructions loads immediate values. These
instructions encode the immediate directly into the instruction stream.
-@deftypefn Instruction {} make-short-immediate u8:@var{dst} i16:@var{low-bits}
+@deftypefn Instruction {} make-short-immediate s8:@var{dst} i16:@var{low-bits}
Make an immediate whose low bits are @var{low-bits}, and whose top bits are
0.
@end deftypefn
-@deftypefn Instruction {} make-long-immediate u24:@var{dst} i32:@var{low-bits}
+@deftypefn Instruction {} make-long-immediate s24:@var{dst} i32:@var{low-bits}
Make an immediate whose low bits are @var{low-bits}, and whose top bits are
0.
@end deftypefn
-@deftypefn Instruction {} make-long-long-immediate u24:@var{dst} a32:@var{high-bits} b32:@var{low-bits}
+@deftypefn Instruction {} make-long-long-immediate s24:@var{dst} a32:@var{high-bits} b32:@var{low-bits}
Make an immediate with @var{high-bits} and @var{low-bits}.
@end deftypefn
@@ -990,7 +1027,7 @@ compiled image. A reference to a string will use
@code{make-non-immediate} to treat a pointer into the compilation unit
as a @code{SCM} value directly.
-@deftypefn Instruction {} make-non-immediate u24:@var{dst} n32:@var{offset}
+@deftypefn Instruction {} make-non-immediate s24:@var{dst} n32:@var{offset}
Load a pointer to statically allocated memory into @var{dst}. The
object's memory is will be found @var{offset} 32-bit words away from the
current instruction pointer. Whether the object is mutable or immutable
@@ -1004,7 +1041,7 @@ initialize them when the compilation unit is loaded, storing them into a
slot in the image. References go indirectly through that slot.
@code{static-ref} is used in this case.
-@deftypefn Instruction {} static-ref u24:@var{dst} s32:@var{offset}
+@deftypefn Instruction {} static-ref s24:@var{dst} r32:@var{offset}
Load a @var{scm} value into @var{dst}. The @var{scm} value will be fetched from
memory, @var{offset} 32-bit words away from the current instruction
pointer. @var{offset} is a signed value.
@@ -1016,7 +1053,7 @@ the case, for example, for a pair containing a non-immediate in one of
its fields. @code{static-ref} and @code{static-patch!} are used in
these situations.
-@deftypefn Instruction {} static-set! u24:@var{src} lo32:@var{offset}
+@deftypefn Instruction {} static-set! s24:@var{src} lo32:@var{offset}
Store a @var{scm} value into memory, @var{offset} 32-bit words away from the
current instruction pointer. @var{offset} is a signed value.
@end deftypefn
@@ -1033,19 +1070,19 @@ case for vectors, strings, uniform vectors, pairs, and procedures with
no free variables. Other kinds of data might need special initializers;
those instructions follow.
-@deftypefn Instruction {} string->number u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} string->number s12:@var{dst} s12:@var{src}
Parse a string in @var{src} to a number, and store in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} string->symbol u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} string->symbol s12:@var{dst} s12:@var{src}
Parse a string in @var{src} to a symbol, and store in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} symbol->keyword u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} symbol->keyword s12:@var{dst} s12:@var{src}
Make a keyword from the symbol in @var{src}, and store it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} load-typed-array u8:@var{dst} u8:@var{type} u8:@var{shape} n32:@var{offset} u32:@var{len}
+@deftypefn Instruction {} load-typed-array s24:@var{dst} x8:@var{_} s24:@var{type} x8:@var{_} s24:@var{shape} n32:@var{offset} u32:@var{len}
Load the contiguous typed array located at @var{offset} 32-bit words away
from the instruction pointer, and store into @var{dst}. @var{len} is a byte
length. @var{offset} is signed.
@@ -1077,7 +1114,7 @@ function, a call to @code{abort-to-prompt} looks like any other function
call.
@end deftypefn
-@deftypefn Instruction {} prompt u24:@var{tag} b1:@var{escape-only?} x7:@var{_} u24:@var{proc-slot} x8:@var{_} l24:@var{handler-offset}
+@deftypefn Instruction {} prompt s24:@var{tag} b1:@var{escape-only?} x7:@var{_} f24:@var{proc-slot} x8:@var{_} l24:@var{handler-offset}
Push a new prompt on the dynamic stack, with a tag from @var{tag} and a
handler at @var{handler-offset} words from the current @var{ip}.
@@ -1096,7 +1133,7 @@ continuation.
@xref{Prompts}, for more information on prompts.
@end deftypefn
-@deftypefn Instruction {} wind u12:@var{winder} u12:@var{unwinder}
+@deftypefn Instruction {} wind s12:@var{winder} s12:@var{unwinder}
Push wind and unwind procedures onto the dynamic stack. Note that
neither are actually called; the compiler should emit calls to wind and
unwind for the normal dynamic-wind control flow. Also note that the
@@ -1109,7 +1146,7 @@ thunks, if it could not prove that to be the case. @xref{Dynamic Wind}.
entry off of the dynamic stack.
@end deftypefn
-@deftypefn Instruction {} push-fluid u12:@var{fluid} u12:@var{value}
+@deftypefn Instruction {} push-fluid s12:@var{fluid} s12:@var{value}
Dynamically bind @var{value} to @var{fluid} by creating a with-fluids
object and pushing that object on the dynamic stack. @xref{Fluids and
Dynamic States}.
@@ -1121,11 +1158,11 @@ the fluid to its previous value. @code{push-fluid} should always be
balanced with @code{pop-fluid}.
@end deftypefn
-@deftypefn Instruction {} fluid-ref u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} fluid-ref s12:@var{dst} s12:@var{src}
Reference the fluid in @var{src}, and place the value in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} fluid-set u12:@var{fluid} u12:@var{val}
+@deftypefn Instruction {} fluid-set s12:@var{fluid} s12:@var{val}
Set the value of the fluid in @var{dst} to the value in @var{src}.
@end deftypefn
@@ -1138,6 +1175,30 @@ Bring the VM to a halt, returning all the values from the stack. Used
in the ``boot continuation'', which is used when entering the VM from C.
@end deftypefn
+@deftypefn Instruction {} push s24:@var{src}
+Bump the stack pointer by one word, and fill it with the value from slot
+@var{src}. The offset to @var{src} is calculated before the stack
+pointer is adjusted.
+@end deftypefn
+
+The @code{push} instruction is used when another instruction is unable
+to address an operand because the operand is encoded with fewer than 24
+bits. In that case, Guile's assembler will transparently emit code that
+temporarily pushes any needed operands onto the stack, emits the
+original instruction to address those now-near variables, then shuffles
+the result (if any) back into place.
+
+@deftypefn Instruction {} pop s24:@var{dst}
+Pop the stack pointer, storing the value that was there in slot
+@var{dst}. The offset to @var{dst} is calculated after the stack
+pointer is adjusted.
+@end deftypefn
+
+@deftypefn Instruction {} drop c24:@var{count}
+Pop the stack pointer by @var{count} words, discarding any values that
+were stored there.
+@end deftypefn
+
@node Inlined Scheme Instructions
@subsubsection Inlined Scheme Instructions
@@ -1147,101 +1208,101 @@ procedures. It tries to inline these small operations to avoid the
overhead of creating new stack frames. This allows the compiler to
optimize better.
-@deftypefn Instruction {} make-vector u8:@var{dst} u8:@var{length} u8:@var{init}
+@deftypefn Instruction {} make-vector s8:@var{dst} s8:@var{length} s8:@var{init}
Make a vector and write it to @var{dst}. The vector will have space for
@var{length} slots. They will be filled with the value in slot
@var{init}.
@end deftypefn
-@deftypefn Instruction {} make-vector/immediate u8:@var{dst} u8:@var{length} u8:@var{init}
+@deftypefn Instruction {} make-vector/immediate s8:@var{dst} s8:@var{length} c8:@var{init}
Make a short vector of known size and write it to @var{dst}. The vector
will have space for @var{length} slots, an immediate value. They will
be filled with the value in slot @var{init}.
@end deftypefn
-@deftypefn Instruction {} vector-length u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} vector-length s12:@var{dst} s12:@var{src}
Store the length of the vector in @var{src} in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} vector-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
+@deftypefn Instruction {} vector-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the item at position @var{idx} in the vector in @var{src}, and
store it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} vector-ref/immediate u8:@var{dst} u8:@var{src} u8:@var{idx}
+@deftypefn Instruction {} vector-ref/immediate s8:@var{dst} s8:@var{src} c8:@var{idx}
Fill @var{dst} with the item @var{idx} elements into the vector at
@var{src}. Useful for building data types using vectors.
@end deftypefn
-@deftypefn Instruction {} vector-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
+@deftypefn Instruction {} vector-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
Store @var{src} into the vector @var{dst} at index @var{idx}.
@end deftypefn
-@deftypefn Instruction {} vector-set!/immediate u8:@var{dst} u8:@var{idx} u8:@var{src}
+@deftypefn Instruction {} vector-set!/immediate s8:@var{dst} c8:@var{idx} s8:@var{src}
Store @var{src} into the vector @var{dst} at index @var{idx}. Here
@var{idx} is an immediate value.
@end deftypefn
-@deftypefn Instruction {} struct-vtable u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} struct-vtable s12:@var{dst} s12:@var{src}
Store the vtable of @var{src} into @var{dst}.
@end deftypefn
-@deftypefn Instruction {} allocate-struct u8:@var{dst} u8:@var{vtable} u8:@var{nfields}
+@deftypefn Instruction {} allocate-struct s8:@var{dst} s8:@var{vtable} s8:@var{nfields}
Allocate a new struct with @var{vtable}, and place it in @var{dst}. The
struct will be constructed with space for @var{nfields} fields, which
should correspond to the field count of the @var{vtable}.
@end deftypefn
-@deftypefn Instruction {} struct-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
+@deftypefn Instruction {} struct-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the item at slot @var{idx} in the struct in @var{src}, and store
it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} struct-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
+@deftypefn Instruction {} struct-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
Store @var{src} into the struct @var{dst} at slot @var{idx}.
@end deftypefn
-@deftypefn Instruction {} allocate-struct/immediate u8:@var{dst} u8:@var{vtable} u8:@var{nfields}
-@deftypefnx Instruction {} struct-ref/immediate u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} struct-set!/immediate u8:@var{dst} u8:@var{idx} u8:@var{src}
+@deftypefn Instruction {} allocate-struct/immediate s8:@var{dst} s8:@var{vtable} c8:@var{nfields}
+@deftypefnx Instruction {} struct-ref/immediate s8:@var{dst} s8:@var{src} c8:@var{idx}
+@deftypefnx Instruction {} struct-set!/immediate s8:@var{dst} c8:@var{idx} s8:@var{src}
Variants of the struct instructions, but in which the @var{nfields} or
@var{idx} fields are immediate values.
@end deftypefn
-@deftypefn Instruction {} class-of u12:@var{dst} u12:@var{type}
+@deftypefn Instruction {} class-of s12:@var{dst} s12:@var{type}
Store the vtable of @var{src} into @var{dst}.
@end deftypefn
-@deftypefn Instruction {} make-array u8:@var{dst} u8:@var{type} u8:@var{fill} x8:@var{_} u24:@var{bounds}
+@deftypefn Instruction {} make-array s24:@var{dst} x8:@var{_} s24:@var{type} x8:@var{_} s24:@var{fill} x8:@var{_} s24:@var{bounds}
Make a new array with @var{type}, @var{fill}, and @var{bounds}, storing it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} string-length u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} string-length s12:@var{dst} s12:@var{src}
Store the length of the string in @var{src} in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} string-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
+@deftypefn Instruction {} string-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the character at position @var{idx} in the string in @var{src}, and store
it in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} cons u8:@var{dst} u8:@var{car} u8:@var{cdr}
+@deftypefn Instruction {} cons s8:@var{dst} s8:@var{car} s8:@var{cdr}
Cons @var{car} and @var{cdr}, and store the result in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} car u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} car s12:@var{dst} s12:@var{src}
Place the car of @var{src} in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} cdr u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} cdr s12:@var{dst} s12:@var{src}
Place the cdr of @var{src} in @var{dst}.
@end deftypefn
-@deftypefn Instruction {} set-car! u12:@var{pair} u12:@var{car}
+@deftypefn Instruction {} set-car! s12:@var{pair} s12:@var{car}
Set the car of @var{dst} to @var{src}.
@end deftypefn
-@deftypefn Instruction {} set-cdr! u12:@var{pair} u12:@var{cdr}
+@deftypefn Instruction {} set-cdr! s12:@var{pair} s12:@var{cdr}
Set the cdr of @var{dst} to @var{src}.
@end deftypefn
@@ -1262,55 +1323,55 @@ More instructions could be added here over time.
All of these operations place their result in their first operand,
@var{dst}.
-@deftypefn Instruction {} add u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} add s8:@var{dst} s8:@var{a} s8:@var{b}
Add @var{a} to @var{b}.
@end deftypefn
-@deftypefn Instruction {} add1 u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} add1 s12:@var{dst} s12:@var{src}
Add 1 to the value in @var{src}.
@end deftypefn
-@deftypefn Instruction {} sub u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} sub s8:@var{dst} s8:@var{a} s8:@var{b}
Subtract @var{b} from @var{a}.
@end deftypefn
-@deftypefn Instruction {} sub1 u12:@var{dst} u12:@var{src}
+@deftypefn Instruction {} sub1 s12:@var{dst} s12:@var{src}
Subtract 1 from @var{src}.
@end deftypefn
-@deftypefn Instruction {} mul u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} mul s8:@var{dst} s8:@var{a} s8:@var{b}
Multiply @var{a} and @var{b}.
@end deftypefn
-@deftypefn Instruction {} div u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} div s8:@var{dst} s8:@var{a} s8:@var{b}
Divide @var{a} by @var{b}.
@end deftypefn
-@deftypefn Instruction {} quo u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} quo s8:@var{dst} s8:@var{a} s8:@var{b}
Divide @var{a} by @var{b}.
@end deftypefn
-@deftypefn Instruction {} rem u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} rem s8:@var{dst} s8:@var{a} s8:@var{b}
Divide @var{a} by @var{b}.
@end deftypefn
-@deftypefn Instruction {} mod u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} mod s8:@var{dst} s8:@var{a} s8:@var{b}
Compute the modulo of @var{a} by @var{b}.
@end deftypefn
-@deftypefn Instruction {} ash u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} ash s8:@var{dst} s8:@var{a} s8:@var{b}
Shift @var{a} arithmetically by @var{b} bits.
@end deftypefn
-@deftypefn Instruction {} logand u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} logand s8:@var{dst} s8:@var{a} s8:@var{b}
Compute the bitwise @code{and} of @var{a} and @var{b}.
@end deftypefn
-@deftypefn Instruction {} logior u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} logior s8:@var{dst} s8:@var{a} s8:@var{b}
Compute the bitwise inclusive @code{or} of @var{a} with @var{b}.
@end deftypefn
-@deftypefn Instruction {} logxor u8:@var{dst} u8:@var{a} u8:@var{b}
+@deftypefn Instruction {} logxor s8:@var{dst} s8:@var{a} s8:@var{b}
Compute the bitwise exclusive @code{or} of @var{a} with @var{b}.
@end deftypefn
@@ -1324,31 +1385,31 @@ a clear path for eventual native compilation. Without this, Scheme
programs would need other primitives for accessing raw bytes -- but
these primitives are as good as any.
-@deftypefn Instruction {} bv-u8-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-s8-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-u16-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-s16-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-u32-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-s32-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-u64-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-s64-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-f32-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
-@deftypefnx Instruction {} bv-f64-ref u8:@var{dst} u8:@var{src} u8:@var{idx}
+@deftypefn Instruction {} bv-u8-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-s8-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-u16-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-s16-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-u32-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-s32-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-u64-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-s64-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-f32-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
+@deftypefnx Instruction {} bv-f64-ref s8:@var{dst} s8:@var{src} s8:@var{idx}
Fetch the item at byte offset @var{idx} in the bytevector @var{src}, and
store it in @var{dst}. All accesses use native endianness.
@end deftypefn
-@deftypefn Instruction {} bv-u8-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-s8-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-u16-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-s16-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-u32-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-s32-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-u64-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-s64-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-f32-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
-@deftypefnx Instruction {} bv-f64-set! u8:@var{dst} u8:@var{idx} u8:@var{src}
+@deftypefn Instruction {} bv-u8-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-s8-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-u16-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-s16-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-u32-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-s32-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-u64-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-s64-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-f32-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
+@deftypefnx Instruction {} bv-f64-set! s8:@var{dst} s8:@var{idx} s8:@var{src}
Store @var{src} into the bytevector @var{dst} at byte offset @var{idx}.
Multibyte values are written using native endianness.
diff --git a/module/system/vm/disassembler.scm b/module/system/vm/disassembler.scm
index b76433ba5..233ba759b 100644
--- a/module/system/vm/disassembler.scm
+++ b/module/system/vm/disassembler.scm
@@ -219,7 +219,13 @@ address of that offset."
(list "~S" (unpack-scm (logior (ash high 32) low))))
(('assert-nargs-ee/locals nargs locals)
;; The nargs includes the procedure.
- (list "~a arg~:p, ~a local~:p" (1- nargs) locals))
+ (list "~a slot~:p (~a arg~:p)" (+ locals nargs) (1- nargs)))
+ (('alloc-frame nlocals)
+ (list "~a slot~:p" nlocals))
+ (('reset-frame nlocals)
+ (list "~a slot~:p" nlocals))
+ (('bind-rest dst)
+ (list "~a slot~:p" (1+ dst)))
(('tail-call nargs proc)
(list "~a arg~:p" nargs))
(('make-closure dst target nfree)
From 9144f50c319845828515ceb313609da9e827ffa6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 11:35:07 +0000
Subject: [PATCH 064/865] subr-call implementation simplification
* libguile/vm-engine.c (subr-call): Reference args from SP, not FP.
---
libguile/vm-engine.c | 45 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 23 deletions(-)
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index ca369bd99..308c04cd9 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -801,53 +801,52 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
SYNC_IP ();
- // FIXME!!!!
switch (FRAME_LOCALS_COUNT_FROM (1))
{
case 0:
ret = subr ();
break;
case 1:
- ret = subr (FP_REF (1));
+ ret = subr (SP_REF (0));
break;
case 2:
- ret = subr (FP_REF (1), FP_REF (2));
+ ret = subr (SP_REF (1), SP_REF (0));
break;
case 3:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3));
+ ret = subr (SP_REF (2), SP_REF (1), SP_REF (0));
break;
case 4:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4));
+ ret = subr (SP_REF (3), SP_REF (2), SP_REF (1),
+ SP_REF (0));
break;
case 5:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5));
+ ret = subr (SP_REF (4), SP_REF (3), SP_REF (2),
+ SP_REF (1), SP_REF (0));
break;
case 6:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5), FP_REF (6));
+ ret = subr (SP_REF (5), SP_REF (4), SP_REF (3),
+ SP_REF (2), SP_REF (1), SP_REF (0));
break;
case 7:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5), FP_REF (6),
- FP_REF (7));
+ ret = subr (SP_REF (6), SP_REF (5), SP_REF (4),
+ SP_REF (3), SP_REF (2), SP_REF (1),
+ SP_REF (0));
break;
case 8:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5), FP_REF (6),
- FP_REF (7), FP_REF (8));
+ ret = subr (SP_REF (7), SP_REF (6), SP_REF (5),
+ SP_REF (4), SP_REF (3), SP_REF (2),
+ SP_REF (1), SP_REF (0));
break;
case 9:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5), FP_REF (6),
- FP_REF (7), FP_REF (8), FP_REF (9));
+ ret = subr (SP_REF (8), SP_REF (7), SP_REF (6),
+ SP_REF (5), SP_REF (4), SP_REF (3),
+ SP_REF (2), SP_REF (1), SP_REF (0));
break;
case 10:
- ret = subr (FP_REF (1), FP_REF (2), FP_REF (3),
- FP_REF (4), FP_REF (5), FP_REF (6),
- FP_REF (7), FP_REF (8), FP_REF (9),
- FP_REF (10));
+ ret = subr (SP_REF (9), SP_REF (8), SP_REF (7),
+ SP_REF (6), SP_REF (5), SP_REF (4),
+ SP_REF (3), SP_REF (2), SP_REF (1),
+ SP_REF (0));
break;
default:
abort ();
From 8832e8b68c528fe48e65902692abe713730dd68e Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 12:13:37 +0000
Subject: [PATCH 065/865] Small subr-call refactor
* libguile/gsubr.c (scm_apply_subr): New internal helper.
* libguile/vm-engine.c (subr-call): Call out to scm_apply_subr.
* doc/ref/vm.texi (subr-call): Don't specify how the foreign pointer is
obtained.
---
doc/ref/vm.texi | 7 ++---
libguile/gsubr.c | 43 ++++++++++++++++++++++++-
libguile/gsubr.h | 6 +++-
libguile/vm-engine.c | 74 +++++---------------------------------------
4 files changed, 58 insertions(+), 72 deletions(-)
diff --git a/doc/ref/vm.texi b/doc/ref/vm.texi
index 45c3928a0..e44f21169 100644
--- a/doc/ref/vm.texi
+++ b/doc/ref/vm.texi
@@ -888,10 +888,9 @@ compiler probably shouldn't emit code with these instructions. However,
it's still interesting to know how these things work, so we document
these trampoline instructions here.
-@deftypefn Instruction {} subr-call c24:@var{ptr-idx}
-Call a subr, passing all locals in this frame as arguments. Fetch the
-foreign pointer from @var{ptr-idx}, a free variable. Return from the
-calling frame.
+@deftypefn Instruction {} subr-call x24:@var{_}
+Call a subr, passing all locals in this frame as arguments. Return from
+the calling frame.
@end deftypefn
@deftypefn Instruction {} foreign-call c12:@var{cif-idx} c12:@var{ptr-idx}
diff --git a/libguile/gsubr.c b/libguile/gsubr.c
index 329241da2..a3b804bb5 100644
--- a/libguile/gsubr.c
+++ b/libguile/gsubr.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995-2001, 2006, 2008-2011, 2013
+/* Copyright (C) 1995-2001, 2006, 2008-2011, 2013, 2015
* Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
@@ -295,6 +295,47 @@ scm_i_primitive_call_ip (SCM subr)
return (scm_t_uintptr)(code + (code[3] ? 3 : code[2] ? 2 : 1));
}
+SCM
+scm_apply_subr (union scm_vm_stack_element *sp, scm_t_ptrdiff nslots)
+{
+ SCM (*subr)() = SCM_SUBRF (sp[nslots - 1].as_scm);
+
+#define ARG(i) (sp[i].as_scm)
+ switch (nslots - 1)
+ {
+ case 0:
+ return subr ();
+ case 1:
+ return subr (ARG (0));
+ case 2:
+ return subr (ARG (1), ARG (0));
+ case 3:
+ return subr (ARG (2), ARG (1), ARG (0));
+ case 4:
+ return subr (ARG (3), ARG (2), ARG (1), ARG (0));
+ case 5:
+ return subr (ARG (4), ARG (3), ARG (2), ARG (1), ARG (0));
+ case 6:
+ return subr (ARG (5), ARG (4), ARG (3), ARG (2), ARG (1),
+ ARG (0));
+ case 7:
+ return subr (ARG (6), ARG (5), ARG (4), ARG (3), ARG (2),
+ ARG (1), ARG (0));
+ case 8:
+ return subr (ARG (7), ARG (6), ARG (5), ARG (4), ARG (3),
+ ARG (2), ARG (1), ARG (0));
+ case 9:
+ return subr (ARG (8), ARG (7), ARG (6), ARG (5), ARG (4),
+ ARG (3), ARG (2), ARG (1), ARG (0));
+ case 10:
+ return subr (ARG (9), ARG (8), ARG (7), ARG (6), ARG (5),
+ ARG (4), ARG (3), ARG (2), ARG (1), ARG (0));
+ default:
+ abort ();
+ }
+#undef ARG
+}
+
SCM
scm_c_make_gsubr (const char *name, int req, int opt, int rst, SCM (*fcn)())
{
diff --git a/libguile/gsubr.h b/libguile/gsubr.h
index 065b94766..a9db85e44 100644
--- a/libguile/gsubr.h
+++ b/libguile/gsubr.h
@@ -4,7 +4,7 @@
#define SCM_GSUBR_H
/* Copyright (C) 1995, 1996, 1998, 2000, 2001, 2006, 2008, 2009,
- * 2010, 2011, 2013 Free Software Foundation, Inc.
+ * 2010, 2011, 2013, 2015 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
@@ -57,6 +57,10 @@
SCM_INTERNAL int scm_i_primitive_arity (SCM subr, int *req, int *opt, int *rest);
SCM_INTERNAL scm_t_uintptr scm_i_primitive_call_ip (SCM subr);
+union scm_vm_stack_element;
+SCM_INTERNAL SCM scm_apply_subr (union scm_vm_stack_element *sp,
+ scm_t_ptrdiff nargs);
+
SCM_API SCM scm_c_make_gsubr (const char *name,
int req, int opt, int rst, scm_t_subr fcn);
SCM_API SCM scm_c_make_gsubr_with_generic (const char *name,
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 308c04cd9..d5f68578d 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -781,77 +781,19 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
* Specialized call stubs
*/
- /* subr-call ptr-idx:24
+ /* subr-call _:24
*
- * Call a subr, passing all locals in this frame as arguments. Fetch
- * the foreign pointer from PTR-IDX, a free variable. Return from the
- * calling frame. This instruction is part of the trampolines
- * created in gsubr.c, and is not generated by the compiler.
+ * Call a subr, passing all locals in this frame as arguments. Return
+ * from the calling frame. This instruction is part of the
+ * trampolines created in gsubr.c, and is not generated by the
+ * compiler.
*/
- VM_DEFINE_OP (10, subr_call, "subr-call", OP1 (X8_C24))
+ VM_DEFINE_OP (10, subr_call, "subr-call", OP1 (X32))
{
- scm_t_uint32 ptr_idx;
- SCM pointer, ret;
- SCM (*subr)();
-
- UNPACK_24 (op, ptr_idx);
-
- pointer = SCM_PROGRAM_FREE_VARIABLE_REF (FP_REF (0), ptr_idx);
- subr = SCM_POINTER_VALUE (pointer);
+ SCM ret;
SYNC_IP ();
-
- switch (FRAME_LOCALS_COUNT_FROM (1))
- {
- case 0:
- ret = subr ();
- break;
- case 1:
- ret = subr (SP_REF (0));
- break;
- case 2:
- ret = subr (SP_REF (1), SP_REF (0));
- break;
- case 3:
- ret = subr (SP_REF (2), SP_REF (1), SP_REF (0));
- break;
- case 4:
- ret = subr (SP_REF (3), SP_REF (2), SP_REF (1),
- SP_REF (0));
- break;
- case 5:
- ret = subr (SP_REF (4), SP_REF (3), SP_REF (2),
- SP_REF (1), SP_REF (0));
- break;
- case 6:
- ret = subr (SP_REF (5), SP_REF (4), SP_REF (3),
- SP_REF (2), SP_REF (1), SP_REF (0));
- break;
- case 7:
- ret = subr (SP_REF (6), SP_REF (5), SP_REF (4),
- SP_REF (3), SP_REF (2), SP_REF (1),
- SP_REF (0));
- break;
- case 8:
- ret = subr (SP_REF (7), SP_REF (6), SP_REF (5),
- SP_REF (4), SP_REF (3), SP_REF (2),
- SP_REF (1), SP_REF (0));
- break;
- case 9:
- ret = subr (SP_REF (8), SP_REF (7), SP_REF (6),
- SP_REF (5), SP_REF (4), SP_REF (3),
- SP_REF (2), SP_REF (1), SP_REF (0));
- break;
- case 10:
- ret = subr (SP_REF (9), SP_REF (8), SP_REF (7),
- SP_REF (6), SP_REF (5), SP_REF (4),
- SP_REF (3), SP_REF (2), SP_REF (1),
- SP_REF (0));
- break;
- default:
- abort ();
- }
-
+ ret = scm_apply_subr (sp, FRAME_LOCALS_COUNT ());
CACHE_SP ();
if (SCM_UNLIKELY (SCM_VALUESP (ret)))
From 139ef2d17c98488fc8e5444bf642699f2ad09e08 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 13:03:51 +0000
Subject: [PATCH 066/865] Minor NEWS update.
---
NEWS | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/NEWS b/NEWS
index 208ec9ebd..604113470 100644
--- a/NEWS
+++ b/NEWS
@@ -132,10 +132,10 @@ Guile's compiler now uses a Continuation-Passing Style (CPS)
intermediate language, allowing it to reason easily about temporary
values and control flow. Examples of optimizations that this permits
are optimal contification, optimal common subexpression elimination,
-dead code elimination, parallel moves with at most one temporary,
-allocation of stack slots using precise liveness information, and
-closure optimization. For more, see "Continuation-Passing Style" in the
-manual.
+dead code elimination, loop-invariant code motion, loop peeling, loop
+inversion, parallel moves with at most one temporary, allocation of
+stack slots using precise liveness information, and closure
+optimization. For more, see "Continuation-Passing Style" in the manual.
** Faster interpreter
From 2d4da30fdefbcdb065d4b1f48f2a77d06f69e3c3 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 13:10:30 +0000
Subject: [PATCH 067/865] Update Gnulib to v0.1-603-g1d16a7b
---
GNUmakefile | 2 +-
build-aux/announce-gen | 24 +-
build-aux/config.rpath | 18 +-
build-aux/gendocs.sh | 115 +--
build-aux/git-version-gen | 11 +-
build-aux/gitlog-to-changelog | 180 +++--
build-aux/gnu-web-doc-update | 19 +-
build-aux/gnupload | 2 +-
build-aux/snippet/arg-nonnull.h | 2 +-
build-aux/snippet/c++defs.h | 2 +-
build-aux/snippet/unused-parameter.h | 2 +-
build-aux/snippet/warn-on-use.h | 2 +-
build-aux/useless-if-before-free | 2 +-
build-aux/vc-list-files | 2 +-
doc/gendocs_template | 20 +-
gnulib-local/build-aux/git-version-gen.diff | 14 +-
lib/Makefile.am | 75 +-
lib/accept.c | 2 +-
lib/alignof.h | 2 +-
lib/alloca.in.h | 2 +-
lib/arpa_inet.in.h | 2 +-
lib/asnprintf.c | 2 +-
lib/assure.h | 37 +
lib/basename-lgpl.c | 2 +-
lib/binary-io.c | 1 +
lib/binary-io.h | 2 +-
lib/bind.c | 2 +-
lib/btowc.c | 2 +-
lib/byteswap.in.h | 2 +-
lib/c-ctype.c | 394 +----------
lib/c-ctype.h | 453 +++++++-----
lib/c-strcase.h | 2 +-
lib/c-strcasecmp.c | 2 +-
lib/c-strcaseeq.h | 5 +-
lib/c-strncasecmp.c | 2 +-
lib/canonicalize-lgpl.c | 2 +-
lib/ceil.c | 2 +-
lib/close.c | 2 +-
lib/config.charset | 6 +-
lib/connect.c | 2 +-
lib/copysign.c | 2 +-
lib/dirent.in.h | 4 +-
lib/dirfd.c | 2 +-
lib/dirname-lgpl.c | 2 +-
lib/dirname.h | 10 +-
lib/dosname.h | 2 +-
lib/dup2.c | 2 +-
lib/duplocale.c | 2 +-
lib/errno.in.h | 2 +-
lib/fcntl.in.h | 22 +-
lib/fd-hook.c | 2 +-
lib/fd-hook.h | 2 +-
lib/float+.h | 2 +-
lib/float.c | 2 +-
lib/float.in.h | 2 +-
lib/flock.c | 2 +-
lib/floor.c | 2 +-
lib/frexp.c | 2 +-
lib/fstat.c | 2 +-
lib/fsync.c | 2 +-
lib/full-read.c | 2 +-
lib/full-read.h | 3 +-
lib/full-write.c | 2 +-
lib/full-write.h | 2 +-
lib/gai_strerror.c | 2 +-
lib/getaddrinfo.c | 2 +-
lib/getlogin.c | 2 +-
lib/getpeername.c | 2 +-
lib/getsockname.c | 2 +-
lib/getsockopt.c | 2 +-
lib/gettext.h | 2 +-
lib/gettimeofday.c | 2 +-
lib/glthread/lock.h | 2 +-
lib/iconv.c | 2 +-
lib/iconv.in.h | 2 +-
lib/iconv_close.c | 2 +-
lib/iconv_open-aix.h | 2 +-
lib/iconv_open-hpux.h | 4 +-
lib/iconv_open-irix.h | 4 +-
lib/iconv_open-osf.h | 4 +-
lib/iconv_open.c | 2 +-
lib/iconveh.h | 2 +-
lib/ignore-value.h | 37 -
lib/inet_ntop.c | 2 +-
lib/inet_pton.c | 2 +-
lib/isfinite.c | 2 +-
lib/isinf.c | 2 +-
lib/isnan.c | 2 +-
lib/isnand-nolibm.h | 2 +-
lib/isnand.c | 2 +-
lib/isnanf-nolibm.h | 2 +-
lib/isnanf.c | 2 +-
lib/isnanl-nolibm.h | 2 +-
lib/isnanl.c | 2 +-
lib/itold.c | 2 +-
lib/langinfo.in.h | 20 +-
lib/link.c | 2 +-
lib/listen.c | 2 +-
lib/localcharset.c | 81 ++-
lib/localcharset.h | 2 +-
lib/locale.in.h | 2 +-
lib/localeconv.c | 2 +-
lib/log.c | 2 +-
lib/log1p.c | 2 +-
lib/lstat.c | 2 +-
lib/malloc.c | 2 +-
lib/malloca.c | 2 +-
lib/malloca.h | 2 +-
lib/math.c | 1 +
lib/math.in.h | 2 +-
lib/mbrtowc.c | 9 +-
lib/mbsinit.c | 2 +-
lib/mbtowc-impl.h | 2 +-
lib/mbtowc.c | 2 +-
lib/memchr.c | 2 +-
lib/mkdir.c | 2 +-
lib/mkstemp.c | 2 +-
lib/mktime-internal.h | 4 +
lib/mktime.c | 741 ++++++++++++++++++++
lib/msvc-inval.c | 2 +-
lib/msvc-inval.h | 2 +-
lib/msvc-nothrow.c | 2 +-
lib/msvc-nothrow.h | 2 +-
lib/netdb.in.h | 2 +-
lib/netinet_in.in.h | 2 +-
lib/nl_langinfo.c | 255 ++++---
lib/nproc.c | 2 +-
lib/nproc.h | 2 +-
lib/open.c | 2 +-
lib/pathmax.h | 2 +-
lib/pipe.c | 2 +-
lib/pipe2.c | 2 +-
lib/poll.c | 83 +--
lib/poll.in.h | 2 +-
lib/printf-args.c | 2 +-
lib/printf-args.h | 2 +-
lib/printf-parse.c | 2 +-
lib/printf-parse.h | 2 +-
lib/putenv.c | 2 +-
lib/raise.c | 2 +-
lib/read.c | 2 +-
lib/readlink.c | 2 +-
lib/recv.c | 2 +-
lib/recvfrom.c | 2 +-
lib/ref-add.sin | 2 +-
lib/ref-del.sin | 2 +-
lib/regcomp.c | 44 +-
lib/regex.c | 2 +-
lib/regex.h | 19 +-
lib/regex_internal.c | 23 +-
lib/regex_internal.h | 35 +-
lib/regexec.c | 10 +-
lib/rename.c | 8 +-
lib/rmdir.c | 2 +-
lib/round.c | 2 +-
lib/safe-read.c | 2 +-
lib/safe-read.h | 2 +-
lib/safe-write.c | 2 +-
lib/safe-write.h | 2 +-
lib/same-inode.h | 2 +-
lib/secure_getenv.c | 2 +-
lib/select.c | 38 +-
lib/send.c | 2 +-
lib/sendto.c | 2 +-
lib/setenv.c | 2 +-
lib/setsockopt.c | 2 +-
lib/shutdown.c | 2 +-
lib/signal.in.h | 10 +-
lib/signbitd.c | 2 +-
lib/signbitf.c | 2 +-
lib/signbitl.c | 2 +-
lib/size_max.h | 2 +-
lib/snprintf.c | 2 +-
lib/socket.c | 2 +-
lib/sockets.c | 9 +-
lib/sockets.h | 12 +-
lib/stat-time.h | 18 +-
lib/stat.c | 2 +-
lib/stdalign.in.h | 21 +-
lib/stdbool.in.h | 2 +-
lib/stddef.in.h | 54 +-
lib/stdint.in.h | 5 +-
lib/stdio.in.h | 12 +-
lib/stdlib.in.h | 25 +-
lib/strdup.c | 2 +-
lib/streq.h | 2 +-
lib/strftime.c | 122 ++--
lib/strftime.h | 9 +-
lib/striconveh.c | 2 +-
lib/striconveh.h | 2 +-
lib/string.in.h | 23 +-
lib/stripslash.c | 2 +-
lib/sys_file.in.h | 2 +-
lib/sys_select.in.h | 17 +-
lib/sys_socket.c | 1 +
lib/sys_socket.in.h | 2 +-
lib/sys_stat.in.h | 2 +-
lib/sys_time.in.h | 2 +-
lib/sys_times.in.h | 2 +-
lib/sys_types.in.h | 4 +-
lib/sys_uio.in.h | 2 +-
lib/tempname.c | 120 ++--
lib/tempname.h | 17 +-
lib/time-internal.h | 49 ++
lib/time.in.h | 31 +-
lib/time_r.c | 2 +-
lib/time_rz.c | 323 +++++++++
lib/timegm.c | 38 +
lib/times.c | 4 +-
lib/trunc.c | 2 +-
lib/unistd.c | 1 +
lib/unistd.in.h | 39 +-
lib/unistr.in.h | 2 +-
lib/unistr/u8-mbtouc-aux.c | 2 +-
lib/unistr/u8-mbtouc-unsafe-aux.c | 2 +-
lib/unistr/u8-mbtouc-unsafe.c | 2 +-
lib/unistr/u8-mbtouc.c | 2 +-
lib/unistr/u8-mbtoucr.c | 2 +-
lib/unistr/u8-prev.c | 2 +-
lib/unistr/u8-uctomb-aux.c | 2 +-
lib/unistr/u8-uctomb.c | 2 +-
lib/unitypes.in.h | 2 +-
lib/unsetenv.c | 127 ++++
lib/vasnprintf.c | 199 +++---
lib/vasnprintf.h | 2 +-
lib/verify.h | 2 +-
lib/vsnprintf.c | 2 +-
lib/w32sock.h | 2 +-
lib/wchar.in.h | 11 +-
lib/wcrtomb.c | 2 +-
lib/wctype.in.h | 2 +-
lib/write.c | 2 +-
lib/xsize.h | 2 +-
m4/00gnulib.m4 | 2 +-
m4/absolute-header.m4 | 2 +-
m4/alloca.m4 | 2 +-
m4/arpa_inet_h.m4 | 2 +-
m4/autobuild.m4 | 2 +-
m4/btowc.m4 | 2 +-
m4/byteswap.m4 | 2 +-
m4/canonicalize.m4 | 2 +-
m4/ceil.m4 | 2 +-
m4/check-math-lib.m4 | 2 +-
m4/clock_time.m4 | 2 +-
m4/close.m4 | 2 +-
m4/configmake.m4 | 2 +-
m4/copysign.m4 | 2 +-
m4/dirent_h.m4 | 2 +-
m4/dirfd.m4 | 2 +-
m4/dirname.m4 | 2 +-
m4/double-slash-root.m4 | 2 +-
m4/dup2.m4 | 84 ++-
m4/duplocale.m4 | 2 +-
m4/eealloc.m4 | 2 +-
m4/environ.m4 | 2 +-
m4/errno_h.m4 | 2 +-
m4/exponentd.m4 | 2 +-
m4/exponentf.m4 | 2 +-
m4/exponentl.m4 | 2 +-
m4/extensions.m4 | 7 +-
m4/extern-inline.m4 | 45 +-
m4/fcntl-o.m4 | 2 +-
m4/fcntl_h.m4 | 2 +-
m4/flexmember.m4 | 41 ++
m4/float_h.m4 | 2 +-
m4/flock.m4 | 2 +-
m4/floor.m4 | 2 +-
m4/fpieee.m4 | 4 +-
m4/frexp.m4 | 2 +-
m4/fstat.m4 | 2 +-
m4/fsync.m4 | 2 +-
m4/func.m4 | 2 +-
m4/getaddrinfo.m4 | 2 +-
m4/getlogin.m4 | 2 +-
m4/gettimeofday.m4 | 2 +-
m4/glibc21.m4 | 2 +-
m4/gnulib-cache.m4 | 5 +-
m4/gnulib-common.m4 | 105 +--
m4/gnulib-comp.m4 | 65 +-
m4/gnulib-tool.m4 | 2 +-
m4/hostent.m4 | 2 +-
m4/iconv.m4 | 61 +-
m4/iconv_h.m4 | 2 +-
m4/iconv_open-utf.m4 | 2 +-
m4/iconv_open.m4 | 2 +-
m4/include_next.m4 | 2 +-
m4/inet_ntop.m4 | 2 +-
m4/inet_pton.m4 | 2 +-
m4/inline.m4 | 2 +-
m4/intmax_t.m4 | 2 +-
m4/inttypes_h.m4 | 2 +-
m4/isfinite.m4 | 40 +-
m4/isinf.m4 | 43 +-
m4/isnan.m4 | 2 +-
m4/isnand.m4 | 2 +-
m4/isnanf.m4 | 2 +-
m4/isnanl.m4 | 43 +-
m4/langinfo_h.m4 | 2 +-
m4/largefile.m4 | 2 +-
m4/ld-version-script.m4 | 47 +-
m4/ldexp.m4 | 2 +-
m4/lib-ld.m4 | 2 +-
m4/lib-link.m4 | 2 +-
m4/lib-prefix.m4 | 2 +-
m4/libunistring-base.m4 | 2 +-
m4/libunistring.m4 | 2 +-
m4/link.m4 | 2 +-
m4/localcharset.m4 | 2 +-
m4/locale-fr.m4 | 2 +-
m4/locale-ja.m4 | 2 +-
m4/locale-zh.m4 | 2 +-
m4/locale_h.m4 | 2 +-
m4/localeconv.m4 | 2 +-
m4/log.m4 | 2 +-
m4/log1p.m4 | 2 +-
m4/longlong.m4 | 2 +-
m4/lstat.m4 | 50 +-
m4/malloc.m4 | 2 +-
m4/malloca.m4 | 2 +-
m4/math_h.m4 | 2 +-
m4/mathfunc.m4 | 2 +-
m4/mbrtowc.m4 | 48 +-
m4/mbsinit.m4 | 2 +-
m4/mbstate_t.m4 | 2 +-
m4/mbtowc.m4 | 2 +-
m4/memchr.m4 | 2 +-
m4/mkdir.m4 | 2 +-
m4/mkstemp.m4 | 2 +-
m4/mktime.m4 | 253 +++++++
m4/mmap-anon.m4 | 2 +-
m4/mode_t.m4 | 2 +-
m4/msvc-inval.m4 | 2 +-
m4/msvc-nothrow.m4 | 2 +-
m4/multiarch.m4 | 2 +-
m4/netdb_h.m4 | 2 +-
m4/netinet_in_h.m4 | 2 +-
m4/nl_langinfo.m4 | 2 +-
m4/nocrash.m4 | 2 +-
m4/nproc.m4 | 2 +-
m4/off_t.m4 | 2 +-
m4/open.m4 | 2 +-
m4/pathmax.m4 | 2 +-
m4/pipe.m4 | 2 +-
m4/pipe2.m4 | 2 +-
m4/poll.m4 | 2 +-
m4/poll_h.m4 | 2 +-
m4/printf.m4 | 67 +-
m4/putenv.m4 | 2 +-
m4/raise.m4 | 2 +-
m4/read.m4 | 2 +-
m4/readlink.m4 | 2 +-
m4/regex.m4 | 2 +-
m4/rename.m4 | 2 +-
m4/rmdir.m4 | 2 +-
m4/round.m4 | 2 +-
m4/safe-read.m4 | 2 +-
m4/safe-write.m4 | 2 +-
m4/secure_getenv.m4 | 2 +-
m4/select.m4 | 2 +-
m4/servent.m4 | 2 +-
m4/setenv.m4 | 2 +-
m4/signal_h.m4 | 2 +-
m4/signbit.m4 | 2 +-
m4/size_max.m4 | 2 +-
m4/snprintf.m4 | 2 +-
m4/socketlib.m4 | 2 +-
m4/sockets.m4 | 2 +-
m4/socklen.m4 | 2 +-
m4/sockpfaf.m4 | 2 +-
m4/ssize_t.m4 | 2 +-
m4/stat-time.m4 | 2 +-
m4/stat.m4 | 2 +-
m4/stdalign.m4 | 10 +-
m4/stdbool.m4 | 2 +-
m4/stddef_h.m4 | 10 +-
m4/stdint.m4 | 2 +-
m4/stdint_h.m4 | 2 +-
m4/stdio_h.m4 | 31 +-
m4/stdlib_h.m4 | 4 +-
m4/strdup.m4 | 2 +-
m4/strftime.m4 | 2 +-
m4/string_h.m4 | 2 +-
m4/sys_file_h.m4 | 2 +-
m4/sys_select_h.m4 | 2 +-
m4/sys_socket_h.m4 | 2 +-
m4/sys_stat_h.m4 | 2 +-
m4/sys_time_h.m4 | 3 +-
m4/sys_times_h.m4 | 2 +-
m4/sys_types_h.m4 | 2 +-
m4/sys_uio_h.m4 | 2 +-
m4/tempname.m4 | 2 +-
m4/time_h.m4 | 22 +-
m4/time_r.m4 | 2 +-
m4/time_rz.m4 | 21 +
m4/timegm.m4 | 26 +
m4/times.m4 | 2 +-
m4/tm_gmtoff.m4 | 2 +-
m4/trunc.m4 | 2 +-
m4/unistd_h.m4 | 6 +-
m4/vasnprintf.m4 | 2 +-
m4/visibility.m4 | 2 +-
m4/vsnprintf.m4 | 2 +-
m4/warn-on-use.m4 | 2 +-
m4/warnings.m4 | 2 +-
m4/wchar_h.m4 | 2 +-
m4/wchar_t.m4 | 2 +-
m4/wcrtomb.m4 | 2 +-
m4/wctype_h.m4 | 2 +-
m4/wint_t.m4 | 2 +-
m4/write.m4 | 2 +-
m4/xsize.m4 | 2 +-
maint.mk | 48 +-
412 files changed, 3961 insertions(+), 2000 deletions(-)
create mode 100644 lib/assure.h
delete mode 100644 lib/ignore-value.h
create mode 100644 lib/mktime-internal.h
create mode 100644 lib/mktime.c
create mode 100644 lib/time-internal.h
create mode 100644 lib/time_rz.c
create mode 100644 lib/timegm.c
create mode 100644 lib/unsetenv.c
create mode 100644 m4/flexmember.m4
create mode 100644 m4/mktime.m4
create mode 100644 m4/time_rz.m4
create mode 100644 m4/timegm.m4
diff --git a/GNUmakefile b/GNUmakefile
index 4ab642943..6617eec2e 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -5,7 +5,7 @@
# It is necessary if you want to build targets usually of interest
# only to the maintainer.
-# Copyright (C) 2001, 2003, 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2003, 2006-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/announce-gen b/build-aux/announce-gen
index db9ed50a7..8a6edb5d4 100755
--- a/build-aux/announce-gen
+++ b/build-aux/announce-gen
@@ -3,13 +3,13 @@ eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
if 0;
# Generate a release announcement message.
-my $VERSION = '2012-06-08 06:53'; # UTC
+my $VERSION = '2013-07-09 06:39'; # UTC
# The definition above must lie within the first 8 lines in order
# for the Emacs time-stamp write hook (at end) to update it.
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -29,15 +29,18 @@ my $VERSION = '2012-06-08 06:53'; # UTC
use strict;
use Getopt::Long;
-use Digest::MD5;
-eval { require Digest::SHA; }
- or eval 'use Digest::SHA1';
use POSIX qw(strftime);
(my $ME = $0) =~ s|.*/||;
my %valid_release_types = map {$_ => 1} qw (alpha beta stable);
my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz');
+my %digest_classes =
+ (
+ 'md5' => (eval { require Digest::MD5; } and 'Digest::MD5'),
+ 'sha1' => ((eval { require Digest::SHA; } and 'Digest::SHA')
+ or (eval { require Digest::SHA1; } and 'Digest::SHA1'))
+ );
my $srcdir = '.';
sub usage ($)
@@ -157,15 +160,13 @@ sub print_checksums (@)
foreach my $meth (qw (md5 sha1))
{
+ my $class = $digest_classes{$meth} or next;
foreach my $f (@file)
{
open IN, '<', $f
or die "$ME: $f: cannot open for reading: $!\n";
binmode IN;
- my $dig =
- ($meth eq 'md5'
- ? Digest::MD5->new->addfile(*IN)->hexdigest
- : Digest::SHA1->new->addfile(*IN)->hexdigest);
+ my $dig = $class->new->addfile(*IN)->hexdigest;
close IN;
print "$dig $f\n";
}
@@ -416,14 +417,15 @@ sub get_tool_versions ($$)
@url_dir_list
or (warn "URL directory name(s) not specified\n"), $fail = 1;
- my @tool_list = split ',', $bootstrap_tools;
+ my @tool_list = split ',', $bootstrap_tools
+ if $bootstrap_tools;
grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version
and (warn "when specifying gnulib as a tool, you must also specify\n"
. "--gnulib-version=V, where V is the result of running git describe\n"
. "in the gnulib source directory.\n"), $fail = 1;
- exists $valid_release_types{$release_type}
+ !$release_type || exists $valid_release_types{$release_type}
or (warn "'$release_type': invalid release type\n"), $fail = 1;
@ARGV
diff --git a/build-aux/config.rpath b/build-aux/config.rpath
index ab6fd995f..a3e25c844 100755
--- a/build-aux/config.rpath
+++ b/build-aux/config.rpath
@@ -2,7 +2,7 @@
# Output a system dependent set of variables, describing how to set the
# run time search path of shared libraries in an executable.
#
-# Copyright 1996-2014 Free Software Foundation, Inc.
+# Copyright 1996-2015 Free Software Foundation, Inc.
# Taken from GNU libtool, 2001
# Originally by Gordon Matzigkeit , 1996
#
@@ -367,11 +367,7 @@ else
dgux*)
hardcode_libdir_flag_spec='-L$libdir'
;;
- freebsd2.2*)
- hardcode_libdir_flag_spec='-R$libdir'
- hardcode_direct=yes
- ;;
- freebsd2*)
+ freebsd2.[01]*)
hardcode_direct=yes
hardcode_minus_L=yes
;;
@@ -548,13 +544,11 @@ case "$host_os" in
dgux*)
library_names_spec='$libname$shrext'
;;
+ freebsd[23].*)
+ library_names_spec='$libname$shrext$versuffix'
+ ;;
freebsd* | dragonfly*)
- case "$host_os" in
- freebsd[123]*)
- library_names_spec='$libname$shrext$versuffix' ;;
- *)
- library_names_spec='$libname$shrext' ;;
- esac
+ library_names_spec='$libname$shrext'
;;
gnu*)
library_names_spec='$libname$shrext'
diff --git a/build-aux/gendocs.sh b/build-aux/gendocs.sh
index f9ec9df76..c8ca1bbc4 100755
--- a/build-aux/gendocs.sh
+++ b/build-aux/gendocs.sh
@@ -2,10 +2,9 @@
# gendocs.sh -- generate a GNU manual in many formats. This script is
# mentioned in maintain.texi. See the help message below for usage details.
-scriptversion=2013-10-10.09
+scriptversion=2015-05-05.16
-# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
-# Free Software Foundation, Inc.
+# Copyright 2003-2015 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,17 +20,16 @@ scriptversion=2013-10-10.09
# along with this program. If not, see .
#
# Original author: Mohit Agarwal.
-# Send bug reports and any other correspondence to bug-texinfo@gnu.org.
+# Send bug reports and any other correspondence to bug-gnulib@gnu.org.
#
# The latest version of this script, and the companion template, is
-# available from Texinfo CVS:
-# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs.sh
-# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs_template
+# available from the Gnulib repository:
#
-# An up-to-date copy is also maintained in Gnulib (gnu.org/software/gnulib).
+# http://git.savannah.gnu.org/cgit/gnulib.git/tree/build-aux/gendocs.sh
+# http://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/gendocs_template
# TODO:
-# - image importation was only implemented for HTML generated by
+# - image importing was only implemented for HTML generated by
# makeinfo. But it should be simple enough to adjust.
# - images are not imported in the source tarball. All the needed
# formats (PDF, PNG, etc.) should be included.
@@ -39,12 +37,12 @@ scriptversion=2013-10-10.09
prog=`basename "$0"`
srcdir=`pwd`
-scripturl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs.sh"
-templateurl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs_template"
+scripturl="http://git.savannah.gnu.org/cgit/gnulib.git/plain/build-aux/gendocs.sh"
+templateurl="http://git.savannah.gnu.org/cgit/gnulib.git/plain/doc/gendocs_template"
: ${SETLANG="env LANG= LC_MESSAGES= LC_ALL= LANGUAGE="}
: ${MAKEINFO="makeinfo"}
-: ${TEXI2DVI="texi2dvi -t @finalout"}
+: ${TEXI2DVI="texi2dvi"}
: ${DOCBOOK2HTML="docbook2html"}
: ${DOCBOOK2PDF="docbook2pdf"}
: ${DOCBOOK2TXT="docbook2txt"}
@@ -56,7 +54,7 @@ unset use_texi2html
version="gendocs.sh $scriptversion
-Copyright 2013 Free Software Foundation, Inc.
+Copyright 2015 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
@@ -75,11 +73,16 @@ Options:
-o OUTDIR write files into OUTDIR, instead of manual/.
-I DIR append DIR to the Texinfo search path.
--common ARG pass ARG in all invocations.
- --html ARG pass ARG to makeinfo or texi2html for HTML targets.
+ --html ARG pass ARG to makeinfo or texi2html for HTML targets,
+ instead of --css-ref=/software/gnulib/manual.css.
--info ARG pass ARG to makeinfo for Info, instead of --no-split.
--no-ascii skip generating the plain text output.
+ --no-html skip generating the html output.
+ --no-info skip generating the info output.
+ --no-tex skip generating the dvi and pdf output.
--source ARG include ARG in tar archive of sources.
--split HOW make split HTML by node, section, chapter; default node.
+ --tex ARG pass ARG to texi2dvi for DVI and PDF, instead of -t @finalout.
--texi2html use texi2html to make HTML target, with all split versions.
--docbook convert through DocBook too (xml, txt, html, pdf).
@@ -131,7 +134,7 @@ locale, since that's the language of most Texinfo manuals. If you
happen to have a non-English manual and non-English web site, see the
SETLANG setting in the source.
-Email bug reports or enhancement requests to bug-texinfo@gnu.org.
+Email bug reports or enhancement requests to bug-gnulib@gnu.org.
"
MANUAL_TITLE=
@@ -139,14 +142,18 @@ PACKAGE=
EMAIL=webmasters@gnu.org # please override with --email
commonarg= # passed to all makeinfo/texi2html invcations.
dirargs= # passed to all tools (-I dir).
-dirs= # -I's directories.
-htmlarg=
+dirs= # -I directories.
+htmlarg=--css-ref=/software/gnulib/manual.css
infoarg=--no-split
generate_ascii=true
+generate_html=true
+generate_info=true
+generate_tex=true
outdir=manual
source_extra=
split=node
srcfile=
+texarg="-t @finalout"
while test $# -gt 0; do
case $1 in
@@ -159,8 +166,12 @@ while test $# -gt 0; do
--html) shift; htmlarg=$1;;
--info) shift; infoarg=$1;;
--no-ascii) generate_ascii=false;;
+ --no-html) generate_ascii=false;;
+ --no-info) generate_info=false;;
+ --no-tex) generate_tex=false;;
--source) shift; source_extra=$1;;
--split) shift; split=$1;;
+ --tex) shift; texarg=$1;;
--texi2html) use_texi2html=1;;
--help) echo "$usage"; exit 0;;
@@ -221,8 +232,9 @@ calcsize()
# copy_images OUTDIR HTML-FILE...
# -------------------------------
-# Copy all the images needed by the HTML-FILEs into OUTDIR. Look
-# for them in the -I directories.
+# Copy all the images needed by the HTML-FILEs into OUTDIR.
+# Look for them in . and the -I directories; this is simpler than what
+# makeinfo supports with -I, but hopefully it will suffice.
copy_images()
{
local odir
@@ -232,7 +244,7 @@ copy_images()
BEGIN {
\$me = '$prog';
\$odir = '$odir';
- @dirs = qw($dirs);
+ @dirs = qw(. $dirs);
}
" -e '
/
${srcdir}/$PACKAGE-db.xml"
@@ -431,7 +457,8 @@ if test -n "$docbook"; then
mv $PACKAGE-db.pdf "$outdir/"
fi
-printf "\nMaking index file...\n"
+#
+printf "\nMaking index.html for $PACKAGE...\n"
if test -z "$use_texi2html"; then
CONDS="/%%IF *HTML_SECTION%%/,/%%ENDIF *HTML_SECTION%%/d;\
/%%IF *HTML_CHAPTER%%/,/%%ENDIF *HTML_CHAPTER%%/d"
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
index 1e5d556e9..8e185a9d1 100755
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
@@ -1,8 +1,8 @@
#!/bin/sh
# Print a version string.
-scriptversion=2012-12-31.23; # UTC
+scriptversion=2014-12-02.19; # UTC
-# Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# Copyright (C) 2007-2015 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -85,9 +85,10 @@ Print a version string.
Options:
- --prefix prefix of git tags (default 'v')
+ --prefix PREFIX prefix of git tags (default 'v')
--match pattern for git tags to match (default: '\$prefix*')
- --fallback fallback version to use if \"git --version\" fails
+ --fallback VERSION
+ fallback version to use if \"git --version\" fails
--help display this help and exit
--version output version information and exit
@@ -220,7 +221,7 @@ if test "x$v_from_git" != x; then
fi
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
-echo "$v" | tr -d "$nl"
+printf %s "$v"
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog
index 78afff4e8..a0e0a05df 100755
--- a/build-aux/gitlog-to-changelog
+++ b/build-aux/gitlog-to-changelog
@@ -3,13 +3,13 @@ eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
if 0;
# Convert git log output to ChangeLog format.
-my $VERSION = '2012-07-29 06:11'; # UTC
+my $VERSION = '2015-06-11 01:03'; # UTC
# The definition above must lie within the first 8 lines in order
# for the Emacs time-stamp write hook (at end) to update it.
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2008-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -72,6 +72,9 @@ OPTIONS:
directory can be derived.
--since=DATE convert only the logs since DATE;
the default is to convert all log entries.
+ --until=DATE convert only the logs older than DATE.
+ --ignore-matching=PAT ignore commit messages whose first lines match PAT.
+ --ignore-line=PAT ignore lines of commit messages that match PAT.
--format=FMT set format string for commit subject and body;
see 'man git-log' for the list of format metacharacters;
the default is '%s%n%b%n'
@@ -220,10 +223,13 @@ sub git_dir_option($)
{
my $since_date;
+ my $until_date;
my $format_string = '%s%n%b%n';
my $amend_file;
my $append_dot = 0;
my $cluster = 1;
+ my $ignore_matching;
+ my $ignore_line;
my $strip_tab = 0;
my $strip_cherry_pick = 0;
my $srcdir;
@@ -232,10 +238,13 @@ sub git_dir_option($)
help => sub { usage 0 },
version => sub { print "$ME version $VERSION\n"; exit },
'since=s' => \$since_date,
+ 'until=s' => \$until_date,
'format=s' => \$format_string,
'amend=s' => \$amend_file,
'append-dot' => \$append_dot,
'cluster!' => \$cluster,
+ 'ignore-matching=s' => \$ignore_matching,
+ 'ignore-line=s' => \$ignore_line,
'strip-tab' => \$strip_tab,
'strip-cherry-pick' => \$strip_cherry_pick,
'srcdir=s' => \$srcdir,
@@ -243,6 +252,8 @@ sub git_dir_option($)
defined $since_date
and unshift @ARGV, "--since=$since_date";
+ defined $until_date
+ and unshift @ARGV, "--until=$until_date";
# This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
# that makes a correction in the log or attribution of that commit.
@@ -259,6 +270,7 @@ sub git_dir_option($)
my $prev_multi_paragraph;
my $prev_date_line = '';
my @prev_coauthors = ();
+ my @skipshas = ();
while (1)
{
defined (my $in = )
@@ -279,6 +291,21 @@ sub git_dir_option($)
$sha =~ /^[0-9a-fA-F]{40}$/
or die "$ME:$.: invalid SHA1: $sha\n";
+ my $skipflag = 0;
+ if (@skipshas)
+ {
+ foreach(@skipshas)
+ {
+ if ($sha =~ /^$_/)
+ {
+ $skipflag = 1;
+ ## Perhaps only warn if a pattern matches more than once?
+ warn "$ME: warning: skipping $sha due to $_\n";
+ last;
+ }
+ }
+ }
+
# If this commit's log requires any transformation, do it now.
my $code = $amend_code->{$sha};
if (defined $code)
@@ -306,7 +333,7 @@ sub git_dir_option($)
$rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m;
}
- my @line = split "\n", $rest;
+ my @line = split /[ \t]*\n/, $rest;
my $author_line = shift @line;
defined $author_line
or die "$ME:$.: unexpected EOF\n";
@@ -316,17 +343,18 @@ sub git_dir_option($)
# Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
# `(tiny change)' annotation.
- my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line)
+ my $tiny = (grep (/^(?:Copyright-paperwork-exempt|Tiny-change):\s+[Yy]es$/, @line)
? ' (tiny change)' : '');
my $date_line = sprintf "%s %s$tiny\n",
- strftime ("%F", localtime ($1)), $2;
+ strftime ("%Y-%m-%d", localtime ($1)), $2;
my @coauthors = grep /^Co-authored-by:.*$/, @line;
# Omit meta-data lines we've already interpreted.
@line = grep !/^(?:Signed-off-by:[ ].*>$
|Co-authored-by:[ ]
|Copyright-paperwork-exempt:[ ]
+ |Tiny-change:[ ]
)/x, @line;
# Remove leading and trailing blank lines.
@@ -336,68 +364,100 @@ sub git_dir_option($)
while ($line[$#line] =~ /^\s*$/) { pop @line; }
}
- # Record whether there are two or more paragraphs.
- my $multi_paragraph = grep /^\s*$/, @line;
+ # Handle Emacs gitmerge.el "skipped" commits.
+ # Yes, this should be controlled by an option. So sue me.
+ if ( grep /^(; )?Merge from /, @line )
+ {
+ my $found = 0;
+ foreach (@line)
+ {
+ if (grep /^The following commit.*skipped:$/, $_)
+ {
+ $found = 1;
+ ## Reset at each merge to reduce chance of false matches.
+ @skipshas = ();
+ next;
+ }
+ if ($found && $_ =~ /^([0-9a-fA-F]{7,}) [^ ]/)
+ {
+ push ( @skipshas, $1 );
+ }
+ }
+ }
- # Format 'Co-authored-by: A U Thor ' lines in
- # standard multi-author ChangeLog format.
- for (@coauthors)
+ # Ignore commits that match the --ignore-matching pattern, if specified.
+ if (! ($skipflag || (defined $ignore_matching
+ && @line && $line[0] =~ /$ignore_matching/)))
{
- s/^Co-authored-by:\s*/\t /;
- s/\s* ;
-
- /<.*?@.*\..*>/
- or warn "$ME: warning: missing email address for "
- . substr ($_, 5) . "\n";
- }
-
- # If clustering of commit messages has been disabled, if this header
- # would be different from the previous date/name/email/coauthors header,
- # or if this or the previous entry consists of two or more paragraphs,
- # then print the header.
- if ( ! $cluster
- || $date_line ne $prev_date_line
- || "@coauthors" ne "@prev_coauthors"
- || $multi_paragraph
- || $prev_multi_paragraph)
- {
- $prev_date_line eq ''
- or print "\n";
- print $date_line;
- @coauthors
- and print join ("\n", @coauthors), "\n";
- }
- $prev_date_line = $date_line;
- @prev_coauthors = @coauthors;
- $prev_multi_paragraph = $multi_paragraph;
-
- # If there were any lines
- if (@line == 0)
- {
- warn "$ME: warning: empty commit message:\n $date_line\n";
- }
- else
- {
- if ($append_dot)
+ if (defined $ignore_line && @line)
{
- # If the first line of the message has enough room, then
- if (length $line[0] < 72)
- {
- # append a dot if there is no other punctuation or blank
- # at the end.
- $line[0] =~ /[[:punct:]\s]$/
- or $line[0] .= '.';
- }
+ @line = grep ! /$ignore_line/, @line;
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
}
- # Remove one additional leading TAB from each line.
- $strip_tab
- and map { s/^\t// } @line;
+ # Record whether there are two or more paragraphs.
+ my $multi_paragraph = grep /^\s*$/, @line;
- # Prefix each non-empty line with a TAB.
- @line = map { length $_ ? "\t$_" : '' } @line;
+ # Format 'Co-authored-by: A U Thor ' lines in
+ # standard multi-author ChangeLog format.
+ for (@coauthors)
+ {
+ s/^Co-authored-by:\s*/\t /;
+ s/\s* ;
- print "\n", join ("\n", @line), "\n";
+ /<.*?@.*\..*>/
+ or warn "$ME: warning: missing email address for "
+ . substr ($_, 5) . "\n";
+ }
+
+ # If clustering of commit messages has been disabled, if this header
+ # would be different from the previous date/name/etc. header,
+ # or if this or the previous entry consists of two or more paragraphs,
+ # then print the header.
+ if ( ! $cluster
+ || $date_line ne $prev_date_line
+ || "@coauthors" ne "@prev_coauthors"
+ || $multi_paragraph
+ || $prev_multi_paragraph)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ @coauthors
+ and print join ("\n", @coauthors), "\n";
+ }
+ $prev_date_line = $date_line;
+ @prev_coauthors = @coauthors;
+ $prev_multi_paragraph = $multi_paragraph;
+
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n $date_line\n";
+ }
+ else
+ {
+ if ($append_dot)
+ {
+ # If the first line of the message has enough room, then
+ if (length $line[0] < 72)
+ {
+ # append a dot if there is no other punctuation or blank
+ # at the end.
+ $line[0] =~ /[[:punct:]\s]$/
+ or $line[0] .= '.';
+ }
+ }
+
+ # Remove one additional leading TAB from each line.
+ $strip_tab
+ and map { s/^\t// } @line;
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
+ }
}
defined ($in = )
diff --git a/build-aux/gnu-web-doc-update b/build-aux/gnu-web-doc-update
index 7af2f185f..c8fc7e5de 100755
--- a/build-aux/gnu-web-doc-update
+++ b/build-aux/gnu-web-doc-update
@@ -2,9 +2,9 @@
# Run this after each non-alpha release, to update the web documentation at
# http://www.gnu.org/software/$pkg/manual/
-VERSION=2012-12-16.14; # UTC
+VERSION=2015-06-16.06; # UTC
-# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2009-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ assumes all documentation is in the doc/ sub-directory.
Options:
-C, --builddir=DIR location of (configured) Makefile (default: .)
-n, --dry-run don't actually commit anything
+ -m, --mirror remove out of date files from document server
--help print this help, then exit
--version print version number, then exit
@@ -107,6 +108,7 @@ find_tool XARGS gxargs xargs
builddir=.
dryrun=
+rm_stale='echo'
while test $# != 0
do
# Handle --option=value by splitting apart and putting back on argv.
@@ -123,6 +125,7 @@ do
--help|--version) ${1#--};;
-C|--builddir) shift; builddir=$1; shift ;;
-n|--dry-run) dryrun=echo; shift;;
+ -m|--mirror) rm_stale=''; shift;;
--*) die "unrecognized option: $1";;
*) break;;
esac
@@ -159,6 +162,7 @@ $GIT submodule update --recursive
./bootstrap
srcdir=$(pwd)
cd "$builddir"
+builddir=$(pwd)
./config.status --recheck
./config.status
make
@@ -182,6 +186,17 @@ $RSYNC -avP "$builddir"/doc/manual/ $tmp/$pkg/manual
find . -name CVS -prune -o -print \
| $XARGS --no-run-if-empty -- $dryrun $CVS add -ko
+ # Report/Remove stale files
+ # excluding doc server specific files like CVS/* and .symlinks
+ if test -n "$rm_stale"; then
+ echo 'Consider the --mirror option if all of the manual is generated,' >&2
+ echo 'which will run `cvs remove` to remove stale files.' >&2
+ fi
+ { find . \( -name CVS -o -type f -name '.*' \) -prune -o -type f -print
+ (cd "$builddir"/doc/manual/ && find . -type f -print | sed p)
+ } | sort | uniq -u \
+ | $XARGS --no-run-if-empty -- ${rm_stale:-$dryrun} $CVS remove -f
+
$dryrun $CVS ci -m $version
)
diff --git a/build-aux/gnupload b/build-aux/gnupload
index 2da97d894..f87c195dc 100755
--- a/build-aux/gnupload
+++ b/build-aux/gnupload
@@ -3,7 +3,7 @@
scriptversion=2013-03-19.17; # UTC
-# Copyright (C) 2004-2014 Free Software Foundation, Inc.
+# Copyright (C) 2004-2015 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/snippet/arg-nonnull.h b/build-aux/snippet/arg-nonnull.h
index 9ee8b1555..0d55e2bce 100644
--- a/build-aux/snippet/arg-nonnull.h
+++ b/build-aux/snippet/arg-nonnull.h
@@ -1,5 +1,5 @@
/* A C macro for declaring that specific arguments must not be NULL.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/c++defs.h b/build-aux/snippet/c++defs.h
index 67b12335d..585b38ab3 100644
--- a/build-aux/snippet/c++defs.h
+++ b/build-aux/snippet/c++defs.h
@@ -1,5 +1,5 @@
/* C++ compatible function declaration macros.
- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/unused-parameter.h b/build-aux/snippet/unused-parameter.h
index 41d9510ca..f507eb745 100644
--- a/build-aux/snippet/unused-parameter.h
+++ b/build-aux/snippet/unused-parameter.h
@@ -1,5 +1,5 @@
/* A C macro for declaring that specific function parameters are not used.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/warn-on-use.h b/build-aux/snippet/warn-on-use.h
index 1c4d7bd4e..90f4985ce 100644
--- a/build-aux/snippet/warn-on-use.h
+++ b/build-aux/snippet/warn-on-use.h
@@ -1,5 +1,5 @@
/* A C macro for emitting warnings if a function is used.
- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/useless-if-before-free b/build-aux/useless-if-before-free
index 4c76c75d7..82a09b39e 100755
--- a/build-aux/useless-if-before-free
+++ b/build-aux/useless-if-before-free
@@ -10,7 +10,7 @@ my $VERSION = '2012-01-06 07:23'; # UTC
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2008-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/vc-list-files b/build-aux/vc-list-files
index b2bca54c9..3bf93c3c0 100755
--- a/build-aux/vc-list-files
+++ b/build-aux/vc-list-files
@@ -4,7 +4,7 @@
# Print a version string.
scriptversion=2011-05-16.22; # UTC
-# Copyright (C) 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2006-2015 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/doc/gendocs_template b/doc/gendocs_template
index 4836df787..df7faa3d3 100644
--- a/doc/gendocs_template
+++ b/doc/gendocs_template
@@ -1,5 +1,6 @@
-%%TITLE%% - GNU Project - Free Software Foundation (FSF)
+
+%%TITLE%% - GNU Project - Free Software Foundation
%%TITLE%%
@@ -67,19 +68,22 @@ script.)
diff --git a/gnulib-local/build-aux/git-version-gen.diff b/gnulib-local/build-aux/git-version-gen.diff
index f875f49d9..e15acf336 100644
--- a/gnulib-local/build-aux/git-version-gen.diff
+++ b/gnulib-local/build-aux/git-version-gen.diff
@@ -4,15 +4,15 @@ Remove when integrated in Gnulib.
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
-@@ -86,6 +86,7 @@ Print a version string.
+@@ -86,6 +86,7 @@
Options:
- --prefix prefix of git tags (default 'v')
+ --prefix PREFIX prefix of git tags (default 'v')
+ --match pattern for git tags to match (default: '\$prefix*')
- --fallback fallback version to use if \"git --version\" fails
+ --fallback VERSION
+ fallback version to use if \"git --version\" fails
- --help display this help and exit
-@@ -96,11 +97,15 @@ Running without arguments will suffice in most cases."
+@@ -97,11 +98,15 @@
prefix=v
fallback=
@@ -28,7 +28,7 @@ Remove when integrated in Gnulib.
--fallback) shift; fallback="$1";;
-*)
echo "$0: Unknown option '$1'." >&2
-@@ -124,6 +129,7 @@ if test "x$tarball_version_file" = x; then
+@@ -125,6 +130,7 @@
exit 1
fi
@@ -36,7 +36,7 @@ Remove when integrated in Gnulib.
tag_sed_script="${tag_sed_script:-s/x/x/}"
nl='
-@@ -154,7 +160,7 @@ then
+@@ -155,7 +161,7 @@
# directory, and "git describe" output looks sensible, use that to
# derive a version string.
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 5d9c902fc..e5a4aed8b 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,6 +1,6 @@
## DO NOT EDIT! GENERATED AUTOMATICALLY!
## Process this file with automake to produce Makefile.in.
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2015 Free Software Foundation, Inc.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -176,6 +176,13 @@ EXTRA_DIST += arpa_inet.in.h
## end gnulib module arpa_inet
+## begin gnulib module assure
+
+
+EXTRA_DIST += assure.h
+
+## end gnulib module assure
+
## begin gnulib module binary-io
libgnu_la_SOURCES += binary-io.h binary-io.c
@@ -699,6 +706,9 @@ EXTRA_DIST += $(top_srcdir)/build-aux/gnupload
## begin gnulib module gperf
GPERF = gperf
+V_GPERF = $(V_GPERF_@AM_V@)
+V_GPERF_ = $(V_GPERF_@AM_DEFAULT_V@)
+V_GPERF_0 = @echo " GPERF " $@;
## end gnulib module gperf
@@ -748,19 +758,19 @@ EXTRA_DIST += iconv.in.h
## begin gnulib module iconv_open
iconv_open-aix.h: iconv_open-aix.gperf
- $(GPERF) -m 10 $(srcdir)/iconv_open-aix.gperf > $(srcdir)/iconv_open-aix.h-t
+ $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-aix.gperf > $(srcdir)/iconv_open-aix.h-t && \
mv $(srcdir)/iconv_open-aix.h-t $(srcdir)/iconv_open-aix.h
iconv_open-hpux.h: iconv_open-hpux.gperf
- $(GPERF) -m 10 $(srcdir)/iconv_open-hpux.gperf > $(srcdir)/iconv_open-hpux.h-t
+ $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-hpux.gperf > $(srcdir)/iconv_open-hpux.h-t && \
mv $(srcdir)/iconv_open-hpux.h-t $(srcdir)/iconv_open-hpux.h
iconv_open-irix.h: iconv_open-irix.gperf
- $(GPERF) -m 10 $(srcdir)/iconv_open-irix.gperf > $(srcdir)/iconv_open-irix.h-t
+ $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-irix.gperf > $(srcdir)/iconv_open-irix.h-t && \
mv $(srcdir)/iconv_open-irix.h-t $(srcdir)/iconv_open-irix.h
iconv_open-osf.h: iconv_open-osf.gperf
- $(GPERF) -m 10 $(srcdir)/iconv_open-osf.gperf > $(srcdir)/iconv_open-osf.h-t
+ $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-osf.gperf > $(srcdir)/iconv_open-osf.h-t && \
mv $(srcdir)/iconv_open-osf.h-t $(srcdir)/iconv_open-osf.h
iconv_open-solaris.h: iconv_open-solaris.gperf
- $(GPERF) -m 10 $(srcdir)/iconv_open-solaris.gperf > $(srcdir)/iconv_open-solaris.h-t
+ $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-solaris.gperf > $(srcdir)/iconv_open-solaris.h-t && \
mv $(srcdir)/iconv_open-solaris.h-t $(srcdir)/iconv_open-solaris.h
BUILT_SOURCES += iconv_open-aix.h iconv_open-hpux.h iconv_open-irix.h iconv_open-osf.h iconv_open-solaris.h
MOSTLYCLEANFILES += iconv_open-aix.h-t iconv_open-hpux.h-t iconv_open-irix.h-t iconv_open-osf.h-t iconv_open-solaris.h-t
@@ -1446,6 +1456,24 @@ EXTRA_libgnu_la_SOURCES += mkstemp.c
## end gnulib module mkstemp
+## begin gnulib module mktime
+
+
+EXTRA_DIST += mktime-internal.h mktime.c
+
+EXTRA_libgnu_la_SOURCES += mktime.c
+
+## end gnulib module mktime
+
+## begin gnulib module mktime-internal
+
+
+EXTRA_DIST += mktime-internal.h mktime.c
+
+EXTRA_libgnu_la_SOURCES += mktime.c
+
+## end gnulib module mktime-internal
+
## begin gnulib module msvc-inval
@@ -2060,6 +2088,7 @@ stddef.h: stddef.in.h $(top_builddir)/config.status
-e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_STDDEF_H''@|$(NEXT_STDDEF_H)|g' \
+ -e 's|@''HAVE_MAX_ALIGN_T''@|$(HAVE_MAX_ALIGN_T)|g' \
-e 's|@''HAVE_WCHAR_T''@|$(HAVE_WCHAR_T)|g' \
-e 's|@''REPLACE_NULL''@|$(REPLACE_NULL)|g' \
< $(srcdir)/stddef.in.h; \
@@ -2286,6 +2315,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-e 's/@''GNULIB_PTSNAME''@/$(GNULIB_PTSNAME)/g' \
-e 's/@''GNULIB_PTSNAME_R''@/$(GNULIB_PTSNAME_R)/g' \
-e 's/@''GNULIB_PUTENV''@/$(GNULIB_PUTENV)/g' \
+ -e 's/@''GNULIB_QSORT_R''@/$(GNULIB_QSORT_R)/g' \
-e 's/@''GNULIB_RANDOM''@/$(GNULIB_RANDOM)/g' \
-e 's/@''GNULIB_RANDOM_R''@/$(GNULIB_RANDOM_R)/g' \
-e 's/@''GNULIB_REALLOC_POSIX''@/$(GNULIB_REALLOC_POSIX)/g' \
@@ -2337,6 +2367,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-e 's|@''REPLACE_PTSNAME''@|$(REPLACE_PTSNAME)|g' \
-e 's|@''REPLACE_PTSNAME_R''@|$(REPLACE_PTSNAME_R)|g' \
-e 's|@''REPLACE_PUTENV''@|$(REPLACE_PUTENV)|g' \
+ -e 's|@''REPLACE_QSORT_R''@|$(REPLACE_QSORT_R)|g' \
-e 's|@''REPLACE_RANDOM_R''@|$(REPLACE_RANDOM_R)|g' \
-e 's|@''REPLACE_REALLOC''@|$(REPLACE_REALLOC)|g' \
-e 's|@''REPLACE_REALPATH''@|$(REPLACE_REALPATH)|g' \
@@ -2812,10 +2843,12 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(
-e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \
-e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \
-e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \
+ -e 's/@''GNULIB_TIME_RZ''@/$(GNULIB_TIME_RZ)/g' \
-e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \
-e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \
-e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \
-e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \
+ -e 's|@''HAVE_TIMEZONE_T''@|$(HAVE_TIMEZONE_T)|g' \
-e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \
-e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \
-e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \
@@ -2825,6 +2858,7 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(
-e 's|@''PTHREAD_H_DEFINES_STRUCT_TIMESPEC''@|$(PTHREAD_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e 's|@''SYS_TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(SYS_TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e 's|@''TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \
+ -e 's|@''UNISTD_H_DEFINES_STRUCT_TIMESPEC''@|$(UNISTD_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
@@ -2846,6 +2880,24 @@ EXTRA_libgnu_la_SOURCES += time_r.c
## end gnulib module time_r
+## begin gnulib module time_rz
+
+
+EXTRA_DIST += time-internal.h time_rz.c
+
+EXTRA_libgnu_la_SOURCES += time_rz.c
+
+## end gnulib module time_rz
+
+## begin gnulib module timegm
+
+
+EXTRA_DIST += mktime-internal.h timegm.c
+
+EXTRA_libgnu_la_SOURCES += timegm.c
+
+## end gnulib module timegm
+
## begin gnulib module times
@@ -2995,9 +3047,11 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
-e 's|@''REPLACE_PWRITE''@|$(REPLACE_PWRITE)|g' \
-e 's|@''REPLACE_READ''@|$(REPLACE_READ)|g' \
-e 's|@''REPLACE_READLINK''@|$(REPLACE_READLINK)|g' \
+ -e 's|@''REPLACE_READLINKAT''@|$(REPLACE_READLINKAT)|g' \
-e 's|@''REPLACE_RMDIR''@|$(REPLACE_RMDIR)|g' \
-e 's|@''REPLACE_SLEEP''@|$(REPLACE_SLEEP)|g' \
-e 's|@''REPLACE_SYMLINK''@|$(REPLACE_SYMLINK)|g' \
+ -e 's|@''REPLACE_SYMLINKAT''@|$(REPLACE_SYMLINKAT)|g' \
-e 's|@''REPLACE_TTYNAME_R''@|$(REPLACE_TTYNAME_R)|g' \
-e 's|@''REPLACE_UNLINK''@|$(REPLACE_UNLINK)|g' \
-e 's|@''REPLACE_UNLINKAT''@|$(REPLACE_UNLINKAT)|g' \
@@ -3088,6 +3142,15 @@ EXTRA_DIST += unitypes.in.h
## end gnulib module unitypes
+## begin gnulib module unsetenv
+
+
+EXTRA_DIST += unsetenv.c
+
+EXTRA_libgnu_la_SOURCES += unsetenv.c
+
+## end gnulib module unsetenv
+
## begin gnulib module useless-if-before-free
diff --git a/lib/accept.c b/lib/accept.c
index b216c6bd6..441c065db 100644
--- a/lib/accept.c
+++ b/lib/accept.c
@@ -1,6 +1,6 @@
/* accept.c --- wrappers for Windows accept function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/alignof.h b/lib/alignof.h
index 280f3e384..713f3bd89 100644
--- a/lib/alignof.h
+++ b/lib/alignof.h
@@ -1,5 +1,5 @@
/* Determine alignment of types.
- Copyright (C) 2003-2004, 2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2004, 2006, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/alloca.in.h b/lib/alloca.in.h
index e3aa62d2d..dc1b55060 100644
--- a/lib/alloca.in.h
+++ b/lib/alloca.in.h
@@ -1,6 +1,6 @@
/* Memory allocation on the stack.
- Copyright (C) 1995, 1999, 2001-2004, 2006-2014 Free Software Foundation,
+ Copyright (C) 1995, 1999, 2001-2004, 2006-2015 Free Software Foundation,
Inc.
This program is free software; you can redistribute it and/or modify it
diff --git a/lib/arpa_inet.in.h b/lib/arpa_inet.in.h
index 3f5df4776..6ad528cc6 100644
--- a/lib/arpa_inet.in.h
+++ b/lib/arpa_inet.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2005-2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/asnprintf.c b/lib/asnprintf.c
index 7806f6888..413b8d763 100644
--- a/lib/asnprintf.c
+++ b/lib/asnprintf.c
@@ -1,5 +1,5 @@
/* Formatted output to strings.
- Copyright (C) 1999, 2002, 2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2002, 2006, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/assure.h b/lib/assure.h
new file mode 100644
index 000000000..867688e01
--- /dev/null
+++ b/lib/assure.h
@@ -0,0 +1,37 @@
+/* Run-time assert-like macros.
+
+ Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+ This program 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 program 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 program. If not, see . */
+
+/* Written by Paul Eggert. */
+
+#ifndef _GL_ASSURE_H
+#define _GL_ASSURE_H
+
+#include
+
+/* Check E's value at runtime, and report an error and abort if not.
+ However, do nothng if NDEBUG is defined.
+
+ Unlike standard 'assert', this macro always compiles E even when NDEBUG
+ is defined, so as to catch typos and avoid some GCC warnings. */
+
+#ifdef NDEBUG
+# define assure(E) ((void) (0 && (E)))
+#else
+# define assure(E) assert (E)
+#endif
+
+#endif
diff --git a/lib/basename-lgpl.c b/lib/basename-lgpl.c
index fe007936f..12d1e522b 100644
--- a/lib/basename-lgpl.c
+++ b/lib/basename-lgpl.c
@@ -1,6 +1,6 @@
/* basename.c -- return the last element in a file name
- Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2014 Free Software
+ Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2015 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/binary-io.c b/lib/binary-io.c
index 8bbdb44d1..d828bcd01 100644
--- a/lib/binary-io.c
+++ b/lib/binary-io.c
@@ -1,3 +1,4 @@
#include
#define BINARY_IO_INLINE _GL_EXTERN_INLINE
#include "binary-io.h"
+typedef int dummy;
diff --git a/lib/binary-io.h b/lib/binary-io.h
index c276faa88..1a87e8f3b 100644
--- a/lib/binary-io.h
+++ b/lib/binary-io.h
@@ -1,5 +1,5 @@
/* Binary mode I/O.
- Copyright (C) 2001, 2003, 2005, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2003, 2005, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/bind.c b/lib/bind.c
index 36750c9a8..72263cc1b 100644
--- a/lib/bind.c
+++ b/lib/bind.c
@@ -1,6 +1,6 @@
/* bind.c --- wrappers for Windows bind function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/btowc.c b/lib/btowc.c
index aad27f593..01e80c50a 100644
--- a/lib/btowc.c
+++ b/lib/btowc.c
@@ -1,5 +1,5 @@
/* Convert unibyte character to wide character.
- Copyright (C) 2008, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2010-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/byteswap.in.h b/lib/byteswap.in.h
index 130c79dfb..673c53b25 100644
--- a/lib/byteswap.in.h
+++ b/lib/byteswap.in.h
@@ -1,5 +1,5 @@
/* byteswap.h - Byte swapping
- Copyright (C) 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2007, 2009-2015 Free Software Foundation, Inc.
Written by Oskar Liljeblad , 2005.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/c-ctype.c b/lib/c-ctype.c
index 7fe3f7efa..5d9d4d87a 100644
--- a/lib/c-ctype.c
+++ b/lib/c-ctype.c
@@ -1,395 +1,3 @@
-/* Character handling in C locale.
-
- Copyright 2000-2003, 2006, 2009-2014 Free Software Foundation, Inc.
-
-This program 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 2 of the License, or
-(at your option) any later version.
-
-This program 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 program; if not, see . */
-
#include
-
-/* Specification. */
-#define NO_C_CTYPE_MACROS
+#define C_CTYPE_INLINE _GL_EXTERN_INLINE
#include "c-ctype.h"
-
-/* The function isascii is not locale dependent. Its use in EBCDIC is
- questionable. */
-bool
-c_isascii (int c)
-{
- return (c >= 0x00 && c <= 0x7f);
-}
-
-bool
-c_isalnum (int c)
-{
-#if C_CTYPE_CONSECUTIVE_DIGITS \
- && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
- return ((c >= '0' && c <= '9')
- || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z'));
-#else
- return ((c >= '0' && c <= '9')
- || (c >= 'A' && c <= 'Z')
- || (c >= 'a' && c <= 'z'));
-#endif
-#else
- switch (c)
- {
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isalpha (int c)
-{
-#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
- return ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z');
-#else
- return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
-#endif
-#else
- switch (c)
- {
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isblank (int c)
-{
- return (c == ' ' || c == '\t');
-}
-
-bool
-c_iscntrl (int c)
-{
-#if C_CTYPE_ASCII
- return ((c & ~0x1f) == 0 || c == 0x7f);
-#else
- switch (c)
- {
- case ' ': case '!': case '"': case '#': case '$': case '%':
- case '&': case '\'': case '(': case ')': case '*': case '+':
- case ',': case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case ':': case ';': case '<': case '=': case '>': case '?':
- case '@':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '[': case '\\': case ']': case '^': case '_': case '`':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case '{': case '|': case '}': case '~':
- return 0;
- default:
- return 1;
- }
-#endif
-}
-
-bool
-c_isdigit (int c)
-{
-#if C_CTYPE_CONSECUTIVE_DIGITS
- return (c >= '0' && c <= '9');
-#else
- switch (c)
- {
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_islower (int c)
-{
-#if C_CTYPE_CONSECUTIVE_LOWERCASE
- return (c >= 'a' && c <= 'z');
-#else
- switch (c)
- {
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isgraph (int c)
-{
-#if C_CTYPE_ASCII
- return (c >= '!' && c <= '~');
-#else
- switch (c)
- {
- case '!': case '"': case '#': case '$': case '%': case '&':
- case '\'': case '(': case ')': case '*': case '+': case ',':
- case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case ':': case ';': case '<': case '=': case '>': case '?':
- case '@':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '[': case '\\': case ']': case '^': case '_': case '`':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case '{': case '|': case '}': case '~':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isprint (int c)
-{
-#if C_CTYPE_ASCII
- return (c >= ' ' && c <= '~');
-#else
- switch (c)
- {
- case ' ': case '!': case '"': case '#': case '$': case '%':
- case '&': case '\'': case '(': case ')': case '*': case '+':
- case ',': case '-': case '.': case '/':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case ':': case ';': case '<': case '=': case '>': case '?':
- case '@':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '[': case '\\': case ']': case '^': case '_': case '`':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case '{': case '|': case '}': case '~':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_ispunct (int c)
-{
-#if C_CTYPE_ASCII
- return ((c >= '!' && c <= '~')
- && !((c >= '0' && c <= '9')
- || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z')));
-#else
- switch (c)
- {
- case '!': case '"': case '#': case '$': case '%': case '&':
- case '\'': case '(': case ')': case '*': case '+': case ',':
- case '-': case '.': case '/':
- case ':': case ';': case '<': case '=': case '>': case '?':
- case '@':
- case '[': case '\\': case ']': case '^': case '_': case '`':
- case '{': case '|': case '}': case '~':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isspace (int c)
-{
- return (c == ' ' || c == '\t'
- || c == '\n' || c == '\v' || c == '\f' || c == '\r');
-}
-
-bool
-c_isupper (int c)
-{
-#if C_CTYPE_CONSECUTIVE_UPPERCASE
- return (c >= 'A' && c <= 'Z');
-#else
- switch (c)
- {
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-bool
-c_isxdigit (int c)
-{
-#if C_CTYPE_CONSECUTIVE_DIGITS \
- && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
- return ((c >= '0' && c <= '9')
- || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'F'));
-#else
- return ((c >= '0' && c <= '9')
- || (c >= 'A' && c <= 'F')
- || (c >= 'a' && c <= 'f'));
-#endif
-#else
- switch (c)
- {
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- return 1;
- default:
- return 0;
- }
-#endif
-}
-
-int
-c_tolower (int c)
-{
-#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
- return (c >= 'A' && c <= 'Z' ? c - 'A' + 'a' : c);
-#else
- switch (c)
- {
- case 'A': return 'a';
- case 'B': return 'b';
- case 'C': return 'c';
- case 'D': return 'd';
- case 'E': return 'e';
- case 'F': return 'f';
- case 'G': return 'g';
- case 'H': return 'h';
- case 'I': return 'i';
- case 'J': return 'j';
- case 'K': return 'k';
- case 'L': return 'l';
- case 'M': return 'm';
- case 'N': return 'n';
- case 'O': return 'o';
- case 'P': return 'p';
- case 'Q': return 'q';
- case 'R': return 'r';
- case 'S': return 's';
- case 'T': return 't';
- case 'U': return 'u';
- case 'V': return 'v';
- case 'W': return 'w';
- case 'X': return 'x';
- case 'Y': return 'y';
- case 'Z': return 'z';
- default: return c;
- }
-#endif
-}
-
-int
-c_toupper (int c)
-{
-#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
- return (c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c);
-#else
- switch (c)
- {
- case 'a': return 'A';
- case 'b': return 'B';
- case 'c': return 'C';
- case 'd': return 'D';
- case 'e': return 'E';
- case 'f': return 'F';
- case 'g': return 'G';
- case 'h': return 'H';
- case 'i': return 'I';
- case 'j': return 'J';
- case 'k': return 'K';
- case 'l': return 'L';
- case 'm': return 'M';
- case 'n': return 'N';
- case 'o': return 'O';
- case 'p': return 'P';
- case 'q': return 'Q';
- case 'r': return 'R';
- case 's': return 'S';
- case 't': return 'T';
- case 'u': return 'U';
- case 'v': return 'V';
- case 'w': return 'W';
- case 'x': return 'X';
- case 'y': return 'Y';
- case 'z': return 'Z';
- default: return c;
- }
-#endif
-}
diff --git a/lib/c-ctype.h b/lib/c-ctype.h
index a258019f4..c7da46f54 100644
--- a/lib/c-ctype.h
+++ b/lib/c-ctype.h
@@ -5,7 +5,7 @@
functions' behaviour depends on the current locale set via
setlocale.
- Copyright (C) 2000-2003, 2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2000-2003, 2006, 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -25,6 +25,13 @@ along with this program; if not, see . */
#include
+#ifndef _GL_INLINE_HEADER_BEGIN
+ #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef C_CTYPE_INLINE
+# define C_CTYPE_INLINE _GL_INLINE
+#endif
#ifdef __cplusplus
extern "C" {
@@ -39,38 +46,6 @@ extern "C" {
characters. */
-/* Check whether the ASCII optimizations apply. */
-
-/* ANSI C89 (and ISO C99 5.2.1.3 too) already guarantees that
- '0', '1', ..., '9' have consecutive integer values. */
-#define C_CTYPE_CONSECUTIVE_DIGITS 1
-
-#if ('A' <= 'Z') \
- && ('A' + 1 == 'B') && ('B' + 1 == 'C') && ('C' + 1 == 'D') \
- && ('D' + 1 == 'E') && ('E' + 1 == 'F') && ('F' + 1 == 'G') \
- && ('G' + 1 == 'H') && ('H' + 1 == 'I') && ('I' + 1 == 'J') \
- && ('J' + 1 == 'K') && ('K' + 1 == 'L') && ('L' + 1 == 'M') \
- && ('M' + 1 == 'N') && ('N' + 1 == 'O') && ('O' + 1 == 'P') \
- && ('P' + 1 == 'Q') && ('Q' + 1 == 'R') && ('R' + 1 == 'S') \
- && ('S' + 1 == 'T') && ('T' + 1 == 'U') && ('U' + 1 == 'V') \
- && ('V' + 1 == 'W') && ('W' + 1 == 'X') && ('X' + 1 == 'Y') \
- && ('Y' + 1 == 'Z')
-#define C_CTYPE_CONSECUTIVE_UPPERCASE 1
-#endif
-
-#if ('a' <= 'z') \
- && ('a' + 1 == 'b') && ('b' + 1 == 'c') && ('c' + 1 == 'd') \
- && ('d' + 1 == 'e') && ('e' + 1 == 'f') && ('f' + 1 == 'g') \
- && ('g' + 1 == 'h') && ('h' + 1 == 'i') && ('i' + 1 == 'j') \
- && ('j' + 1 == 'k') && ('k' + 1 == 'l') && ('l' + 1 == 'm') \
- && ('m' + 1 == 'n') && ('n' + 1 == 'o') && ('o' + 1 == 'p') \
- && ('p' + 1 == 'q') && ('q' + 1 == 'r') && ('r' + 1 == 's') \
- && ('s' + 1 == 't') && ('t' + 1 == 'u') && ('u' + 1 == 'v') \
- && ('v' + 1 == 'w') && ('w' + 1 == 'x') && ('x' + 1 == 'y') \
- && ('y' + 1 == 'z')
-#define C_CTYPE_CONSECUTIVE_LOWERCASE 1
-#endif
-
#if (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
&& (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
@@ -96,11 +71,84 @@ extern "C" {
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)
/* The character set is ASCII or one of its variants or extensions, not EBCDIC.
Testing the value of '\n' and '\r' is not relevant. */
-#define C_CTYPE_ASCII 1
+# define C_CTYPE_ASCII 1
+#elif ! (' ' == '\x40' && '0' == '\xf0' \
+ && 'A' == '\xc1' && 'J' == '\xd1' && 'S' == '\xe2' \
+ && 'a' == '\x81' && 'j' == '\x91' && 's' == '\xa2')
+# error "Only ASCII and EBCDIC are supported"
#endif
+#if 'A' < 0
+# error "EBCDIC and char is signed -- not supported"
+#endif
-/* Function declarations. */
+/* Cases for control characters. */
+
+#define _C_CTYPE_CNTRL \
+ case '\a': case '\b': case '\f': case '\n': \
+ case '\r': case '\t': case '\v': \
+ _C_CTYPE_OTHER_CNTRL
+
+/* ASCII control characters other than those with \-letter escapes. */
+
+#if C_CTYPE_ASCII
+# define _C_CTYPE_OTHER_CNTRL \
+ case '\x00': case '\x01': case '\x02': case '\x03': \
+ case '\x04': case '\x05': case '\x06': case '\x0e': \
+ case '\x0f': case '\x10': case '\x11': case '\x12': \
+ case '\x13': case '\x14': case '\x15': case '\x16': \
+ case '\x17': case '\x18': case '\x19': case '\x1a': \
+ case '\x1b': case '\x1c': case '\x1d': case '\x1e': \
+ case '\x1f': case '\x7f'
+#else
+ /* Use EBCDIC code page 1047's assignments for ASCII control chars;
+ assume all EBCDIC code pages agree about these assignments. */
+# define _C_CTYPE_OTHER_CNTRL \
+ case '\x00': case '\x01': case '\x02': case '\x03': \
+ case '\x07': case '\x0e': case '\x0f': case '\x10': \
+ case '\x11': case '\x12': case '\x13': case '\x18': \
+ case '\x19': case '\x1c': case '\x1d': case '\x1e': \
+ case '\x1f': case '\x26': case '\x27': case '\x2d': \
+ case '\x2e': case '\x32': case '\x37': case '\x3c': \
+ case '\x3d': case '\x3f'
+#endif
+
+/* Cases for lowercase hex letters, and lowercase letters, all offset by N. */
+
+#define _C_CTYPE_LOWER_A_THRU_F_N(n) \
+ case 'a' + (n): case 'b' + (n): case 'c' + (n): case 'd' + (n): \
+ case 'e' + (n): case 'f' + (n)
+#define _C_CTYPE_LOWER_N(n) \
+ _C_CTYPE_LOWER_A_THRU_F_N(n): \
+ case 'g' + (n): case 'h' + (n): case 'i' + (n): case 'j' + (n): \
+ case 'k' + (n): case 'l' + (n): case 'm' + (n): case 'n' + (n): \
+ case 'o' + (n): case 'p' + (n): case 'q' + (n): case 'r' + (n): \
+ case 's' + (n): case 't' + (n): case 'u' + (n): case 'v' + (n): \
+ case 'w' + (n): case 'x' + (n): case 'y' + (n): case 'z' + (n)
+
+/* Cases for hex letters, digits, lower, punct, and upper. */
+
+#define _C_CTYPE_A_THRU_F \
+ _C_CTYPE_LOWER_A_THRU_F_N (0): \
+ _C_CTYPE_LOWER_A_THRU_F_N ('A' - 'a')
+#define _C_CTYPE_DIGIT \
+ case '0': case '1': case '2': case '3': \
+ case '4': case '5': case '6': case '7': \
+ case '8': case '9'
+#define _C_CTYPE_LOWER _C_CTYPE_LOWER_N (0)
+#define _C_CTYPE_PUNCT \
+ case '!': case '"': case '#': case '$': \
+ case '%': case '&': case '\'': case '(': \
+ case ')': case '*': case '+': case ',': \
+ case '-': case '.': case '/': case ':': \
+ case ';': case '<': case '=': case '>': \
+ case '?': case '@': case '[': case '\\': \
+ case ']': case '^': case '_': case '`': \
+ case '{': case '|': case '}': case '~'
+#define _C_CTYPE_UPPER _C_CTYPE_LOWER_N ('A' - 'a')
+
+
+/* Function definitions. */
/* Unlike the functions in , which require an argument in the range
of the 'unsigned char' type, the functions here operate on values that are
@@ -117,179 +165,202 @@ extern "C" {
if (c_isalpha (*s)) ...
*/
-extern bool c_isascii (int c) _GL_ATTRIBUTE_CONST; /* not locale dependent */
+C_CTYPE_INLINE bool
+c_isalnum (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
-extern bool c_isalnum (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isalpha (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isblank (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_iscntrl (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isdigit (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_islower (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isgraph (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isprint (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_ispunct (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isspace (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isupper (int c) _GL_ATTRIBUTE_CONST;
-extern bool c_isxdigit (int c) _GL_ATTRIBUTE_CONST;
+C_CTYPE_INLINE bool
+c_isalpha (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_LOWER:
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
-extern int c_tolower (int c) _GL_ATTRIBUTE_CONST;
-extern int c_toupper (int c) _GL_ATTRIBUTE_CONST;
+/* The function isascii is not locale dependent.
+ Its use in EBCDIC is questionable. */
+C_CTYPE_INLINE bool
+c_isascii (int c)
+{
+ switch (c)
+ {
+ case ' ':
+ _C_CTYPE_CNTRL:
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
+C_CTYPE_INLINE bool
+c_isblank (int c)
+{
+ return c == ' ' || c == '\t';
+}
-#if (defined __GNUC__ && !defined __STRICT_ANSI__ && defined __OPTIMIZE__ \
- && !defined __OPTIMIZE_SIZE__ && !defined NO_C_CTYPE_MACROS)
+C_CTYPE_INLINE bool
+c_iscntrl (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_CNTRL:
+ return true;
+ default:
+ return false;
+ }
+}
-/* ASCII optimizations. */
+C_CTYPE_INLINE bool
+c_isdigit (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_DIGIT:
+ return true;
+ default:
+ return false;
+ }
+}
-#undef c_isascii
-#define c_isascii(c) \
- ({ int __c = (c); \
- (__c >= 0x00 && __c <= 0x7f); \
- })
+C_CTYPE_INLINE bool
+c_isgraph (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_CONSECUTIVE_DIGITS \
- && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
-#undef c_isalnum
-#define c_isalnum(c) \
- ({ int __c = (c); \
- ((__c >= '0' && __c <= '9') \
- || ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'Z')); \
- })
-#else
-#undef c_isalnum
-#define c_isalnum(c) \
- ({ int __c = (c); \
- ((__c >= '0' && __c <= '9') \
- || (__c >= 'A' && __c <= 'Z') \
- || (__c >= 'a' && __c <= 'z')); \
- })
-#endif
-#endif
+C_CTYPE_INLINE bool
+c_islower (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_LOWER:
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
-#undef c_isalpha
-#define c_isalpha(c) \
- ({ int __c = (c); \
- ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'Z'); \
- })
-#else
-#undef c_isalpha
-#define c_isalpha(c) \
- ({ int __c = (c); \
- ((__c >= 'A' && __c <= 'Z') || (__c >= 'a' && __c <= 'z')); \
- })
-#endif
-#endif
+C_CTYPE_INLINE bool
+c_isprint (int c)
+{
+ switch (c)
+ {
+ case ' ':
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_LOWER:
+ _C_CTYPE_PUNCT:
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
-#undef c_isblank
-#define c_isblank(c) \
- ({ int __c = (c); \
- (__c == ' ' || __c == '\t'); \
- })
+C_CTYPE_INLINE bool
+c_ispunct (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_PUNCT:
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_ASCII
-#undef c_iscntrl
-#define c_iscntrl(c) \
- ({ int __c = (c); \
- ((__c & ~0x1f) == 0 || __c == 0x7f); \
- })
-#endif
+C_CTYPE_INLINE bool
+c_isspace (int c)
+{
+ switch (c)
+ {
+ case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_CONSECUTIVE_DIGITS
-#undef c_isdigit
-#define c_isdigit(c) \
- ({ int __c = (c); \
- (__c >= '0' && __c <= '9'); \
- })
-#endif
+C_CTYPE_INLINE bool
+c_isupper (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_UPPER:
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_CONSECUTIVE_LOWERCASE
-#undef c_islower
-#define c_islower(c) \
- ({ int __c = (c); \
- (__c >= 'a' && __c <= 'z'); \
- })
-#endif
+C_CTYPE_INLINE bool
+c_isxdigit (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_DIGIT:
+ _C_CTYPE_A_THRU_F:
+ return true;
+ default:
+ return false;
+ }
+}
-#if C_CTYPE_ASCII
-#undef c_isgraph
-#define c_isgraph(c) \
- ({ int __c = (c); \
- (__c >= '!' && __c <= '~'); \
- })
-#endif
-
-#if C_CTYPE_ASCII
-#undef c_isprint
-#define c_isprint(c) \
- ({ int __c = (c); \
- (__c >= ' ' && __c <= '~'); \
- })
-#endif
-
-#if C_CTYPE_ASCII
-#undef c_ispunct
-#define c_ispunct(c) \
- ({ int _c = (c); \
- (c_isgraph (_c) && ! c_isalnum (_c)); \
- })
-#endif
-
-#undef c_isspace
-#define c_isspace(c) \
- ({ int __c = (c); \
- (__c == ' ' || __c == '\t' \
- || __c == '\n' || __c == '\v' || __c == '\f' || __c == '\r'); \
- })
-
-#if C_CTYPE_CONSECUTIVE_UPPERCASE
-#undef c_isupper
-#define c_isupper(c) \
- ({ int __c = (c); \
- (__c >= 'A' && __c <= 'Z'); \
- })
-#endif
-
-#if C_CTYPE_CONSECUTIVE_DIGITS \
- && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#if C_CTYPE_ASCII
-#undef c_isxdigit
-#define c_isxdigit(c) \
- ({ int __c = (c); \
- ((__c >= '0' && __c <= '9') \
- || ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'F')); \
- })
-#else
-#undef c_isxdigit
-#define c_isxdigit(c) \
- ({ int __c = (c); \
- ((__c >= '0' && __c <= '9') \
- || (__c >= 'A' && __c <= 'F') \
- || (__c >= 'a' && __c <= 'f')); \
- })
-#endif
-#endif
-
-#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-#undef c_tolower
-#define c_tolower(c) \
- ({ int __c = (c); \
- (__c >= 'A' && __c <= 'Z' ? __c - 'A' + 'a' : __c); \
- })
-#undef c_toupper
-#define c_toupper(c) \
- ({ int __c = (c); \
- (__c >= 'a' && __c <= 'z' ? __c - 'a' + 'A' : __c); \
- })
-#endif
-
-#endif /* optimizing for speed */
+C_CTYPE_INLINE int
+c_tolower (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_UPPER:
+ return c - 'A' + 'a';
+ default:
+ return c;
+ }
+}
+C_CTYPE_INLINE int
+c_toupper (int c)
+{
+ switch (c)
+ {
+ _C_CTYPE_LOWER:
+ return c - 'a' + 'A';
+ default:
+ return c;
+ }
+}
#ifdef __cplusplus
}
#endif
+_GL_INLINE_HEADER_END
+
#endif /* C_CTYPE_H */
diff --git a/lib/c-strcase.h b/lib/c-strcase.h
index ee3bd3f72..55fd801ee 100644
--- a/lib/c-strcase.h
+++ b/lib/c-strcase.h
@@ -1,5 +1,5 @@
/* Case-insensitive string comparison functions in C locale.
- Copyright (C) 1995-1996, 2001, 2003, 2005, 2009-2014 Free Software
+ Copyright (C) 1995-1996, 2001, 2003, 2005, 2009-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/c-strcasecmp.c b/lib/c-strcasecmp.c
index 5059cc659..ce0223fcb 100644
--- a/lib/c-strcasecmp.c
+++ b/lib/c-strcasecmp.c
@@ -1,5 +1,5 @@
/* c-strcasecmp.c -- case insensitive string comparator in C locale
- Copyright (C) 1998-1999, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1998-1999, 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/c-strcaseeq.h b/lib/c-strcaseeq.h
index 44d375148..ce4b82dd1 100644
--- a/lib/c-strcaseeq.h
+++ b/lib/c-strcaseeq.h
@@ -1,5 +1,5 @@
/* Optimized case-insensitive string comparison in C locale.
- Copyright (C) 2001-2002, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
@@ -33,9 +33,6 @@
# if C_CTYPE_ASCII
# define CASEEQ(other,upper) \
(c_isupper (upper) ? ((other) & ~0x20) == (upper) : (other) == (upper))
-# elif C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
-# define CASEEQ(other,upper) \
- (c_isupper (upper) ? (other) == (upper) || (other) == (upper) - 'A' + 'a' : (other) == (upper))
# else
# define CASEEQ(other,upper) \
(c_toupper (other) == (upper))
diff --git a/lib/c-strncasecmp.c b/lib/c-strncasecmp.c
index 614598156..7a7f61df8 100644
--- a/lib/c-strncasecmp.c
+++ b/lib/c-strncasecmp.c
@@ -1,5 +1,5 @@
/* c-strncasecmp.c -- case insensitive string comparator in C locale
- Copyright (C) 1998-1999, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1998-1999, 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/canonicalize-lgpl.c b/lib/canonicalize-lgpl.c
index a999c9c84..624a40eb9 100644
--- a/lib/canonicalize-lgpl.c
+++ b/lib/canonicalize-lgpl.c
@@ -1,5 +1,5 @@
/* Return the canonical absolute name of a given file.
- Copyright (C) 1996-2014 Free Software Foundation, Inc.
+ Copyright (C) 1996-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/ceil.c b/lib/ceil.c
index 7e810357b..236d258b1 100644
--- a/lib/ceil.c
+++ b/lib/ceil.c
@@ -1,5 +1,5 @@
/* Round towards positive infinity.
- Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/close.c b/lib/close.c
index 9d2e0276a..c1f112590 100644
--- a/lib/close.c
+++ b/lib/close.c
@@ -1,5 +1,5 @@
/* close replacement.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/config.charset b/lib/config.charset
index 8fe2507d9..8083c6029 100644
--- a/lib/config.charset
+++ b/lib/config.charset
@@ -1,7 +1,7 @@
#! /bin/sh
# Output a system dependent table of character encoding aliases.
#
-# Copyright (C) 2000-2004, 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2000-2004, 2006-2015 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
@@ -348,12 +348,10 @@ case "$os" in
#echo "sun_eu_greek ?" # what is this?
echo "UTF-8 UTF-8"
;;
- freebsd* | os2*)
+ freebsd*)
# FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore
# localcharset.c falls back to using the full locale name
# from the environment variables.
- # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just
- # reuse FreeBSD's locale data for OS/2.
echo "C ASCII"
echo "US-ASCII ASCII"
for l in la_LN lt_LN; do
diff --git a/lib/connect.c b/lib/connect.c
index 295fe95d8..bde8a60bc 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -1,6 +1,6 @@
/* connect.c --- wrappers for Windows connect function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/copysign.c b/lib/copysign.c
index 616ea356e..761320bc0 100644
--- a/lib/copysign.c
+++ b/lib/copysign.c
@@ -1,5 +1,5 @@
/* Copy sign into another 'double' number.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dirent.in.h b/lib/dirent.in.h
index 3418bd9dc..e4490caf5 100644
--- a/lib/dirent.in.h
+++ b/lib/dirent.in.h
@@ -1,5 +1,5 @@
/* A GNU-like .
- Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -77,6 +77,7 @@ typedef struct gl_directory DIR;
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef opendir
# define opendir rpl_opendir
+# define GNULIB_defined_opendir 1
# endif
_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
@@ -128,6 +129,7 @@ _GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef closedir
# define closedir rpl_closedir
+# define GNULIB_defined_closedir 1
# endif
_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
diff --git a/lib/dirfd.c b/lib/dirfd.c
index 86f8e0a1a..c196c6601 100644
--- a/lib/dirfd.c
+++ b/lib/dirfd.c
@@ -1,6 +1,6 @@
/* dirfd.c -- return the file descriptor associated with an open DIR*
- Copyright (C) 2001, 2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2006, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dirname-lgpl.c b/lib/dirname-lgpl.c
index 121d38754..c1e610109 100644
--- a/lib/dirname-lgpl.c
+++ b/lib/dirname-lgpl.c
@@ -1,6 +1,6 @@
/* dirname.c -- return all but the last element in a file name
- Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2014 Free Software
+ Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2015 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/dirname.h b/lib/dirname.h
index e31cb6190..abb058497 100644
--- a/lib/dirname.h
+++ b/lib/dirname.h
@@ -1,6 +1,6 @@
/* Take file names apart into directory and base names.
- Copyright (C) 1998, 2001, 2003-2006, 2009-2014 Free Software Foundation,
+ Copyright (C) 1998, 2001, 2003-2006, 2009-2015 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
@@ -31,6 +31,10 @@
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
# endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
# if GNULIB_DIRNAME
char *base_name (char const *file);
char *dir_name (char const *file);
@@ -43,4 +47,8 @@ char *last_component (char const *file) _GL_ATTRIBUTE_PURE;
bool strip_trailing_slashes (char *file);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
#endif /* not DIRNAME_H_ */
diff --git a/lib/dosname.h b/lib/dosname.h
index b81163d4b..42e3186ab 100644
--- a/lib/dosname.h
+++ b/lib/dosname.h
@@ -1,6 +1,6 @@
/* File names on MS-DOS/Windows systems.
- Copyright (C) 2000-2001, 2004-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2000-2001, 2004-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dup2.c b/lib/dup2.c
index 9709b7a64..7f984ccb0 100644
--- a/lib/dup2.c
+++ b/lib/dup2.c
@@ -1,6 +1,6 @@
/* Duplicate an open file descriptor to a specified file descriptor.
- Copyright (C) 1999, 2004-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2004-2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/duplocale.c b/lib/duplocale.c
index 86d5ce59a..9fdb26811 100644
--- a/lib/duplocale.c
+++ b/lib/duplocale.c
@@ -1,5 +1,5 @@
/* Duplicate a locale object.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/errno.in.h b/lib/errno.in.h
index 8dbb5f97a..5f1aa8dce 100644
--- a/lib/errno.in.h
+++ b/lib/errno.in.h
@@ -1,6 +1,6 @@
/* A POSIX-like .
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h
index 1cd197002..1c3b2c83f 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -1,6 +1,6 @@
/* Like , but with non-working flags defined to 0.
- Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -34,7 +34,7 @@
extern "C" { ... } block, which leads to errors in C++ mode with the
overridden from gnulib. These errors are known to be gone
with g++ version >= 4.3. */
-#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && (defined __ICC || !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))))
# include
#endif
#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
@@ -53,7 +53,7 @@
extern "C" { ... } block, which leads to errors in C++ mode with the
overridden from gnulib. These errors are known to be gone
with g++ version >= 4.3. */
-#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && (defined __ICC || !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))))
# include
#endif
/* The include_next requires a split double-inclusion guard. */
@@ -186,6 +186,22 @@ _GL_WARN_ON_USE (openat, "openat is not portable - "
/* Fix up the O_* macros. */
+/* AIX 7.1 with XL C 12.1 defines O_CLOEXEC, O_NOFOLLOW, and O_TTY_INIT
+ to values outside 'int' range, so omit these misdefinitions.
+ But avoid namespace pollution on non-AIX systems. */
+#ifdef _AIX
+# include
+# if defined O_CLOEXEC && ! (INT_MIN <= O_CLOEXEC && O_CLOEXEC <= INT_MAX)
+# undef O_CLOEXEC
+# endif
+# if defined O_NOFOLLOW && ! (INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
+# undef O_NOFOLLOW
+# endif
+# if defined O_TTY_INIT && ! (INT_MIN <= O_TTY_INIT && O_TTY_INIT <= INT_MAX)
+# undef O_TTY_INIT
+# endif
+#endif
+
#if !defined O_DIRECT && defined O_DIRECTIO
/* Tru64 spells it 'O_DIRECTIO'. */
# define O_DIRECT O_DIRECTIO
diff --git a/lib/fd-hook.c b/lib/fd-hook.c
index fd07578f1..158b1ecda 100644
--- a/lib/fd-hook.c
+++ b/lib/fd-hook.c
@@ -1,5 +1,5 @@
/* Hook for making making file descriptor functions close(), ioctl() extensible.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2009.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/fd-hook.h b/lib/fd-hook.h
index 5ff0f73fc..7010ccaef 100644
--- a/lib/fd-hook.h
+++ b/lib/fd-hook.h
@@ -1,5 +1,5 @@
/* Hook for making making file descriptor functions close(), ioctl() extensible.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/float+.h b/lib/float+.h
index 085c379b1..c3d28a598 100644
--- a/lib/float+.h
+++ b/lib/float+.h
@@ -1,5 +1,5 @@
/* Supplemental information about the floating-point formats.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2007.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/float.c b/lib/float.c
index 3faa5eede..9a2d0f723 100644
--- a/lib/float.c
+++ b/lib/float.c
@@ -1,5 +1,5 @@
/* Auxiliary definitions for .
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/float.in.h b/lib/float.in.h
index e814eaba5..dab2a2b44 100644
--- a/lib/float.in.h
+++ b/lib/float.in.h
@@ -1,6 +1,6 @@
/* A correct .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/flock.c b/lib/flock.c
index 928e151b0..a591f1e38 100644
--- a/lib/flock.c
+++ b/lib/flock.c
@@ -6,7 +6,7 @@
Written by Richard W.M. Jones
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 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
diff --git a/lib/floor.c b/lib/floor.c
index a00f937ed..010131144 100644
--- a/lib/floor.c
+++ b/lib/floor.c
@@ -1,5 +1,5 @@
/* Round towards negative infinity.
- Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/frexp.c b/lib/frexp.c
index 6eff94574..21edf0afb 100644
--- a/lib/frexp.c
+++ b/lib/frexp.c
@@ -1,5 +1,5 @@
/* Split a double into fraction and mantissa.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fstat.c b/lib/fstat.c
index 17ccc8e29..71ca7b0cb 100644
--- a/lib/fstat.c
+++ b/lib/fstat.c
@@ -1,5 +1,5 @@
/* fstat() replacement.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fsync.c b/lib/fsync.c
index 99475ff65..8ef6dbee8 100644
--- a/lib/fsync.c
+++ b/lib/fsync.c
@@ -7,7 +7,7 @@
Written by Richard W.M. Jones
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 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
diff --git a/lib/full-read.c b/lib/full-read.c
index 4d67afb92..ac7fb5651 100644
--- a/lib/full-read.c
+++ b/lib/full-read.c
@@ -1,5 +1,5 @@
/* An interface to read that retries after partial reads and interrupts.
- Copyright (C) 2002-2003, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/full-read.h b/lib/full-read.h
index 954b94dce..950b9bd3f 100644
--- a/lib/full-read.h
+++ b/lib/full-read.h
@@ -1,6 +1,6 @@
/* An interface to read() that reads all it is asked to read.
- Copyright (C) 2002, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -13,7 +13,6 @@
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
- along with this program; if not, read to the Free Software Foundation,
along with this program. If not, see . */
#include
diff --git a/lib/full-write.c b/lib/full-write.c
index 6a77b7b45..df3bad0d5 100644
--- a/lib/full-write.c
+++ b/lib/full-write.c
@@ -1,6 +1,6 @@
/* An interface to read and write that retries (if necessary) until complete.
- Copyright (C) 1993-1994, 1997-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1993-1994, 1997-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/full-write.h b/lib/full-write.h
index 2fab6fa02..607facd35 100644
--- a/lib/full-write.h
+++ b/lib/full-write.h
@@ -1,6 +1,6 @@
/* An interface to write() that writes all it is asked to write.
- Copyright (C) 2002-2003, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/gai_strerror.c b/lib/gai_strerror.c
index d0c589da1..fbef3bbe9 100644
--- a/lib/gai_strerror.c
+++ b/lib/gai_strerror.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1997, 2001-2002, 2004-2006, 2008-2014 Free Software
+/* Copyright (C) 1997, 2001-2002, 2004-2006, 2008-2015 Free Software
Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Philip Blundell , 1997.
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
index 6581dd55a..9d3384f71 100644
--- a/lib/getaddrinfo.c
+++ b/lib/getaddrinfo.c
@@ -1,5 +1,5 @@
/* Get address information (partial implementation).
- Copyright (C) 1997, 2001-2002, 2004-2014 Free Software Foundation, Inc.
+ Copyright (C) 1997, 2001-2002, 2004-2015 Free Software Foundation, Inc.
Contributed by Simon Josefsson .
This program is free software; you can redistribute it and/or modify
diff --git a/lib/getlogin.c b/lib/getlogin.c
index f8cfe5d78..5fc54b851 100644
--- a/lib/getlogin.c
+++ b/lib/getlogin.c
@@ -1,6 +1,6 @@
/* Provide a working getlogin for systems which lack it.
- Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getpeername.c b/lib/getpeername.c
index e5b3eaea3..ce2e105a6 100644
--- a/lib/getpeername.c
+++ b/lib/getpeername.c
@@ -1,6 +1,6 @@
/* getpeername.c --- wrappers for Windows getpeername function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getsockname.c b/lib/getsockname.c
index d26bae592..6f24e7c18 100644
--- a/lib/getsockname.c
+++ b/lib/getsockname.c
@@ -1,6 +1,6 @@
/* getsockname.c --- wrappers for Windows getsockname function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getsockopt.c b/lib/getsockopt.c
index 0b2fb2b73..ccd2e6fe7 100644
--- a/lib/getsockopt.c
+++ b/lib/getsockopt.c
@@ -1,6 +1,6 @@
/* getsockopt.c --- wrappers for Windows getsockopt function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/gettext.h b/lib/gettext.h
index 330d8dad4..3770ca0e8 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -1,5 +1,5 @@
/* Convenience header for conditional use of GNU .
- Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2014 Free Software
+ Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/gettimeofday.c b/lib/gettimeofday.c
index c4e40fbe9..a615f3b25 100644
--- a/lib/gettimeofday.c
+++ b/lib/gettimeofday.c
@@ -1,6 +1,6 @@
/* Provide gettimeofday for systems that don't have it or for which it's broken.
- Copyright (C) 2001-2003, 2005-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003, 2005-2007, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 66c78a6cd..1aafd8bb4 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -1,7 +1,7 @@
#ifndef SCM_GLTHREADS_H
#define SCM_GLTHREADS_H
-/* Copyright (C) 2014 Free Software Foundation, Inc.
+/* Copyright (C) 2014, 2015 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
diff --git a/lib/iconv.c b/lib/iconv.c
index a6dfed355..850c8b77b 100644
--- a/lib/iconv.c
+++ b/lib/iconv.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 1999-2001, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2001, 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv.in.h b/lib/iconv.in.h
index ed95ed719..6b3d245c9 100644
--- a/lib/iconv.in.h
+++ b/lib/iconv.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv_close.c b/lib/iconv_close.c
index 6e286734d..d728c0741 100644
--- a/lib/iconv_close.c
+++ b/lib/iconv_close.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv_open-aix.h b/lib/iconv_open-aix.h
index a598e819d..129e03084 100644
--- a/lib/iconv_open-aix.h
+++ b/lib/iconv_open-aix.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
+/* ANSI-C code produced by gperf version 3.0.4 */
/* Command-line: gperf -m 10 ./iconv_open-aix.gperf */
/* Computed positions: -k'4,$' */
diff --git a/lib/iconv_open-hpux.h b/lib/iconv_open-hpux.h
index 8f9f0a9ad..3bcff76a7 100644
--- a/lib/iconv_open-hpux.h
+++ b/lib/iconv_open-hpux.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
+/* ANSI-C code produced by gperf version 3.0.4 */
/* Command-line: gperf -m 10 ./iconv_open-hpux.gperf */
/* Computed positions: -k'4,$' */
@@ -272,7 +272,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#ifdef __GNUC_STDC_INLINE__
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open-irix.h b/lib/iconv_open-irix.h
index 520582e52..74ceb8f57 100644
--- a/lib/iconv_open-irix.h
+++ b/lib/iconv_open-irix.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
+/* ANSI-C code produced by gperf version 3.0.4 */
/* Command-line: gperf -m 10 ./iconv_open-irix.gperf */
/* Computed positions: -k'1,$' */
@@ -172,7 +172,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#ifdef __GNUC_STDC_INLINE__
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open-osf.h b/lib/iconv_open-osf.h
index 85e4c0f8f..4d3576495 100644
--- a/lib/iconv_open-osf.h
+++ b/lib/iconv_open-osf.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
+/* ANSI-C code produced by gperf version 3.0.4 */
/* Command-line: gperf -m 10 ./iconv_open-osf.gperf */
/* Computed positions: -k'4,$' */
@@ -251,7 +251,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#ifdef __GNUC_STDC_INLINE__
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open.c b/lib/iconv_open.c
index fc19d44e2..07174331f 100644
--- a/lib/iconv_open.c
+++ b/lib/iconv_open.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconveh.h b/lib/iconveh.h
index 43b23eb39..d02c97502 100644
--- a/lib/iconveh.h
+++ b/lib/iconveh.h
@@ -1,5 +1,5 @@
/* Character set conversion handler type.
- Copyright (C) 2001-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/ignore-value.h b/lib/ignore-value.h
deleted file mode 100644
index 86cfad77b..000000000
--- a/lib/ignore-value.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ignore a function return without a compiler warning
-
- Copyright (C) 2008-2009 Free Software Foundation, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see . */
-
-/* Written by Jim Meyering. */
-
-/* Use these functions to avoid a warning when using a function declared with
- gcc's warn_unused_result attribute, but for which you really do want to
- ignore the result. Traditionally, people have used a "(void)" cast to
- indicate that a function's return value is deliberately unused. However,
- if the function is declared with __attribute__((warn_unused_result)),
- gcc issues a warning even with the cast.
-
- Caution: most of the time, you really should heed gcc's warning, and
- check the return value. However, in those exceptional cases in which
- you're sure you know what you're doing, use this function.
-
- For the record, here's one of the ignorable warnings:
- "copy.c:233: warning: ignoring return value of 'fchown',
- declared with attribute warn_unused_result". */
-
-static inline void ignore_value (int i) { (void) i; }
-static inline void ignore_ptr (void* p) { (void) p; }
-/* FIXME: what about aggregate types? */
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
index 462951968..4620c2c09 100644
--- a/lib/inet_ntop.c
+++ b/lib/inet_ntop.c
@@ -1,6 +1,6 @@
/* inet_ntop.c -- convert IPv4 and IPv6 addresses from binary to text form
- Copyright (C) 2005-2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
index 52ae31784..1fc159a10 100644
--- a/lib/inet_pton.c
+++ b/lib/inet_pton.c
@@ -1,6 +1,6 @@
/* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form
- Copyright (C) 2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isfinite.c b/lib/isfinite.c
index 18c1d217f..3792b9974 100644
--- a/lib/isfinite.c
+++ b/lib/isfinite.c
@@ -1,5 +1,5 @@
/* Test for finite value (zero, subnormal, or normal, and not infinite or NaN).
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isinf.c b/lib/isinf.c
index 217de79df..4c79580e3 100644
--- a/lib/isinf.c
+++ b/lib/isinf.c
@@ -1,5 +1,5 @@
/* Test for positive or negative infinity.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnan.c b/lib/isnan.c
index 1557733bf..d70c9348a 100644
--- a/lib/isnan.c
+++ b/lib/isnan.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnand-nolibm.h b/lib/isnand-nolibm.h
index b0498ef08..f5204f328 100644
--- a/lib/isnand-nolibm.h
+++ b/lib/isnand-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnand.c b/lib/isnand.c
index 11efbf8d8..34e604fb7 100644
--- a/lib/isnand.c
+++ b/lib/isnand.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanf-nolibm.h b/lib/isnanf-nolibm.h
index 9e2aa2f54..b3a280c70 100644
--- a/lib/isnanf-nolibm.h
+++ b/lib/isnanf-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanf.c b/lib/isnanf.c
index c7a66ca3a..7d21bddb4 100644
--- a/lib/isnanf.c
+++ b/lib/isnanf.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanl-nolibm.h b/lib/isnanl-nolibm.h
index 9cf090caa..48a02b24e 100644
--- a/lib/isnanl-nolibm.h
+++ b/lib/isnanl-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanl.c b/lib/isnanl.c
index dbf9d5dd1..9ec4eb31f 100644
--- a/lib/isnanl.c
+++ b/lib/isnanl.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/itold.c b/lib/itold.c
index 136742eab..4963b9b2d 100644
--- a/lib/itold.c
+++ b/lib/itold.c
@@ -1,5 +1,5 @@
/* Replacement for 'int' to 'long double' conversion routine.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/langinfo.in.h b/lib/langinfo.in.h
index f4a281a33..cf992cede 100644
--- a/lib/langinfo.in.h
+++ b/lib/langinfo.in.h
@@ -1,5 +1,5 @@
/* Substitute for and wrapper around .
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -49,7 +49,10 @@ typedef int nl_item;
# define CODESET 10000
/* nl_langinfo items of the LC_NUMERIC category */
# define RADIXCHAR 10001
+# define DECIMAL_POINT RADIXCHAR
# define THOUSEP 10002
+# define THOUSANDS_SEP THOUSEP
+# define GROUPING 10114
/* nl_langinfo items of the LC_TIME category */
# define D_T_FMT 10003
# define D_FMT 10004
@@ -102,6 +105,21 @@ typedef int nl_item;
# define ALT_DIGITS 10051
/* nl_langinfo items of the LC_MONETARY category */
# define CRNCYSTR 10052
+# define CURRENCY_SYMBOL CRNCYSTR
+# define INT_CURR_SYMBOL 10100
+# define MON_DECIMAL_POINT 10101
+# define MON_THOUSANDS_SEP 10102
+# define MON_GROUPING 10103
+# define POSITIVE_SIGN 10104
+# define NEGATIVE_SIGN 10105
+# define FRAC_DIGITS 10106
+# define INT_FRAC_DIGITS 10107
+# define P_CS_PRECEDES 10108
+# define N_CS_PRECEDES 10109
+# define P_SEP_BY_SPACE 10110
+# define N_SEP_BY_SPACE 10111
+# define P_SIGN_POSN 10112
+# define N_SIGN_POSN 10113
/* nl_langinfo items of the LC_MESSAGES category */
# define YESEXPR 10053
# define NOEXPR 10054
diff --git a/lib/link.c b/lib/link.c
index 9db1f8cef..ebdd9f4ef 100644
--- a/lib/link.c
+++ b/lib/link.c
@@ -1,6 +1,6 @@
/* Emulate link on platforms that lack it, namely native Windows platforms.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/listen.c b/lib/listen.c
index 912f1b7a7..7cf092587 100644
--- a/lib/listen.c
+++ b/lib/listen.c
@@ -1,6 +1,6 @@
/* listen.c --- wrappers for Windows listen function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/localcharset.c b/lib/localcharset.c
index 7f09567ce..6dffe3454 100644
--- a/lib/localcharset.c
+++ b/lib/localcharset.c
@@ -1,6 +1,6 @@
/* Determine a canonical name for the current locale's character encoding.
- Copyright (C) 2000-2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2000-2006, 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -34,6 +34,7 @@
#if defined _WIN32 || defined __WIN32__
# define WINDOWS_NATIVE
+# include
#endif
#if defined __EMX__
@@ -127,7 +128,7 @@ get_charset_aliases (void)
cp = charset_aliases;
if (cp == NULL)
{
-#if !(defined DARWIN7 || defined VMS || defined WINDOWS_NATIVE || defined __CYGWIN__)
+#if !(defined DARWIN7 || defined VMS || defined WINDOWS_NATIVE || defined __CYGWIN__ || defined OS2)
const char *dir;
const char *base = "charset.alias";
char *file_name;
@@ -341,6 +342,36 @@ get_charset_aliases (void)
"CP54936" "\0" "GB18030" "\0"
"CP65001" "\0" "UTF-8" "\0";
# endif
+# if defined OS2
+ /* To avoid the troubles of installing a separate file in the same
+ directory as the DLL and of retrieving the DLL's directory at
+ runtime, simply inline the aliases here. */
+
+ /* The list of encodings is taken from "List of OS/2 Codepages"
+ by Alex Taylor:
+ .
+ See also "IBM Globalization - Code page identifiers":
+ . */
+ cp = "CP813" "\0" "ISO-8859-7" "\0"
+ "CP878" "\0" "KOI8-R" "\0"
+ "CP819" "\0" "ISO-8859-1" "\0"
+ "CP912" "\0" "ISO-8859-2" "\0"
+ "CP913" "\0" "ISO-8859-3" "\0"
+ "CP914" "\0" "ISO-8859-4" "\0"
+ "CP915" "\0" "ISO-8859-5" "\0"
+ "CP916" "\0" "ISO-8859-8" "\0"
+ "CP920" "\0" "ISO-8859-9" "\0"
+ "CP921" "\0" "ISO-8859-13" "\0"
+ "CP923" "\0" "ISO-8859-15" "\0"
+ "CP954" "\0" "EUC-JP" "\0"
+ "CP964" "\0" "EUC-TW" "\0"
+ "CP970" "\0" "EUC-KR" "\0"
+ "CP1089" "\0" "ISO-8859-6" "\0"
+ "CP1208" "\0" "UTF-8" "\0"
+ "CP1381" "\0" "GB2312" "\0"
+ "CP1386" "\0" "GBK" "\0"
+ "CP3372" "\0" "EUC-JP" "\0";
+# endif
#endif
charset_aliases = cp;
@@ -461,14 +492,34 @@ locale_charset (void)
static char buf[2 + 10 + 1];
- /* The Windows API has a function returning the locale's codepage as a
- number: GetACP().
- When the output goes to a console window, it needs to be provided in
- GetOEMCP() encoding if the console is using a raster font, or in
- GetConsoleOutputCP() encoding if it is using a TrueType font.
- But in GUI programs and for output sent to files and pipes, GetACP()
- encoding is the best bet. */
- sprintf (buf, "CP%u", GetACP ());
+ /* The Windows API has a function returning the locale's codepage as
+ a number, but the value doesn't change according to what the
+ 'setlocale' call specified. So we use it as a last resort, in
+ case the string returned by 'setlocale' doesn't specify the
+ codepage. */
+ char *current_locale = setlocale (LC_ALL, NULL);
+ char *pdot;
+
+ /* If they set different locales for different categories,
+ 'setlocale' will return a semi-colon separated list of locale
+ values. To make sure we use the correct one, we choose LC_CTYPE. */
+ if (strchr (current_locale, ';'))
+ current_locale = setlocale (LC_CTYPE, NULL);
+
+ pdot = strrchr (current_locale, '.');
+ if (pdot)
+ sprintf (buf, "CP%s", pdot + 1);
+ else
+ {
+ /* The Windows API has a function returning the locale's codepage as a
+ number: GetACP().
+ When the output goes to a console window, it needs to be provided in
+ GetOEMCP() encoding if the console is using a raster font, or in
+ GetConsoleOutputCP() encoding if it is using a TrueType font.
+ But in GUI programs and for output sent to files and pipes, GetACP()
+ encoding is the best bet. */
+ sprintf (buf, "CP%u", GetACP ());
+ }
codeset = buf;
#elif defined OS2
@@ -478,6 +529,8 @@ locale_charset (void)
ULONG cp[3];
ULONG cplen;
+ codeset = NULL;
+
/* Allow user to override the codeset, as set in the operating system,
with standard language environment variables. */
locale = getenv ("LC_ALL");
@@ -509,10 +562,12 @@ locale_charset (void)
}
}
- /* Resolve through the charset.alias file. */
- codeset = locale;
+ /* For the POSIX locale, don't use the system's codepage. */
+ if (strcmp (locale, "C") == 0 || strcmp (locale, "POSIX") == 0)
+ codeset = "";
}
- else
+
+ if (codeset == NULL)
{
/* OS/2 has a function returning the locale's codepage as a number. */
if (DosQueryCp (sizeof (cp), cp, &cplen))
diff --git a/lib/localcharset.h b/lib/localcharset.h
index 4b104c304..86eaec198 100644
--- a/lib/localcharset.h
+++ b/lib/localcharset.h
@@ -1,5 +1,5 @@
/* Determine a canonical name for the current locale's character encoding.
- Copyright (C) 2000-2003, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2000-2003, 2009-2015 Free Software Foundation, Inc.
This file is part of the GNU CHARSET Library.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/locale.in.h b/lib/locale.in.h
index a10b129ca..4a1e229f3 100644
--- a/lib/locale.in.h
+++ b/lib/locale.in.h
@@ -1,5 +1,5 @@
/* A POSIX .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/localeconv.c b/lib/localeconv.c
index ed2767be0..e8fe69d34 100644
--- a/lib/localeconv.c
+++ b/lib/localeconv.c
@@ -1,5 +1,5 @@
/* Query locale dependent information for formatting numbers.
- Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/log.c b/lib/log.c
index ef8d332f8..e09b50c58 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -1,5 +1,5 @@
/* Logarithm.
- Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/log1p.c b/lib/log1p.c
index d1132d3e4..10105f0a8 100644
--- a/lib/log1p.c
+++ b/lib/log1p.c
@@ -1,5 +1,5 @@
/* Natural logarithm of 1 plus argument.
- Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Copyright (C) 2012-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/lstat.c b/lib/lstat.c
index cff1188f3..221bd0a26 100644
--- a/lib/lstat.c
+++ b/lib/lstat.c
@@ -1,6 +1,6 @@
/* Work around a bug of lstat on some systems
- Copyright (C) 1997-2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 1997-2006, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/malloc.c b/lib/malloc.c
index c6e292a74..7622f4d95 100644
--- a/lib/malloc.c
+++ b/lib/malloc.c
@@ -1,6 +1,6 @@
/* malloc() function that is glibc compatible.
- Copyright (C) 1997-1998, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1997-1998, 2006-2007, 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/malloca.c b/lib/malloca.c
index 3e95f2333..ef07acd7f 100644
--- a/lib/malloca.c
+++ b/lib/malloca.c
@@ -1,5 +1,5 @@
/* Safe automatic memory allocation.
- Copyright (C) 2003, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2003.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/malloca.h b/lib/malloca.h
index 5810afa54..37b106ffb 100644
--- a/lib/malloca.h
+++ b/lib/malloca.h
@@ -1,5 +1,5 @@
/* Safe automatic memory allocation.
- Copyright (C) 2003-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2003.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/math.c b/lib/math.c
index ddb2ded53..ba2a6abd6 100644
--- a/lib/math.c
+++ b/lib/math.c
@@ -1,3 +1,4 @@
#include
#define _GL_MATH_INLINE _GL_EXTERN_INLINE
#include "math.h"
+typedef int dummy;
diff --git a/lib/math.in.h b/lib/math.in.h
index 4f2aa862b..b3803f8d0 100644
--- a/lib/math.in.h
+++ b/lib/math.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2002-2003, 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/mbrtowc.c b/lib/mbrtowc.c
index dff12962d..a5d61a066 100644
--- a/lib/mbrtowc.c
+++ b/lib/mbrtowc.c
@@ -1,5 +1,5 @@
/* Convert multibyte character to wide character.
- Copyright (C) 1999-2002, 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2002, 2005-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
@@ -328,7 +328,7 @@ mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
size_t
rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
{
-# if MBRTOWC_NULL_ARG2_BUG || MBRTOWC_RETVAL_BUG
+# if MBRTOWC_NULL_ARG2_BUG || MBRTOWC_RETVAL_BUG || MBRTOWC_EMPTY_INPUT_BUG
if (s == NULL)
{
pwc = NULL;
@@ -337,6 +337,11 @@ rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
}
# endif
+# if MBRTOWC_EMPTY_INPUT_BUG
+ if (n == 0)
+ return (size_t) -2;
+# endif
+
# if MBRTOWC_RETVAL_BUG
{
static mbstate_t internal_state;
diff --git a/lib/mbsinit.c b/lib/mbsinit.c
index 71bae341b..59997834e 100644
--- a/lib/mbsinit.c
+++ b/lib/mbsinit.c
@@ -1,5 +1,5 @@
/* Test for initial conversion state.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/mbtowc-impl.h b/lib/mbtowc-impl.h
index df11ad2bf..737d7633a 100644
--- a/lib/mbtowc-impl.h
+++ b/lib/mbtowc-impl.h
@@ -1,5 +1,5 @@
/* Convert multibyte character to wide character.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/mbtowc.c b/lib/mbtowc.c
index bd9d3aa6b..be0d60992 100644
--- a/lib/mbtowc.c
+++ b/lib/mbtowc.c
@@ -1,5 +1,5 @@
/* Convert multibyte character to wide character.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/memchr.c b/lib/memchr.c
index c1caad3a2..a815ce78a 100644
--- a/lib/memchr.c
+++ b/lib/memchr.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1993, 1996-1997, 1999-2000, 2003-2004, 2006, 2008-2014
+/* Copyright (C) 1991, 1993, 1996-1997, 1999-2000, 2003-2004, 2006, 2008-2015
Free Software Foundation, Inc.
Based on strlen implementation by Torbjorn Granlund (tege@sics.se),
diff --git a/lib/mkdir.c b/lib/mkdir.c
index f1b802b57..c76c57e1f 100644
--- a/lib/mkdir.c
+++ b/lib/mkdir.c
@@ -1,7 +1,7 @@
/* On some systems, mkdir ("foo/", 0700) fails because of the trailing
slash. On those systems, this wrapper removes the trailing slash.
- Copyright (C) 2001, 2003, 2006, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2003, 2006, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/mkstemp.c b/lib/mkstemp.c
index 0af69f9c3..3c0ee9eba 100644
--- a/lib/mkstemp.c
+++ b/lib/mkstemp.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998-1999, 2001, 2005-2007, 2009-2014 Free Software
+/* Copyright (C) 1998-1999, 2001, 2005-2007, 2009-2015 Free Software
Foundation, Inc.
This file is derived from the one in the GNU C Library.
diff --git a/lib/mktime-internal.h b/lib/mktime-internal.h
new file mode 100644
index 000000000..4287acf4a
--- /dev/null
+++ b/lib/mktime-internal.h
@@ -0,0 +1,4 @@
+#include
+time_t mktime_internal (struct tm *,
+ struct tm * (*) (time_t const *, struct tm *),
+ time_t *);
diff --git a/lib/mktime.c b/lib/mktime.c
new file mode 100644
index 000000000..7b125a79e
--- /dev/null
+++ b/lib/mktime.c
@@ -0,0 +1,741 @@
+/* Convert a 'struct tm' to a time_t value.
+ Copyright (C) 1993-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Paul Eggert .
+
+ The GNU C 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 2.1 of the License, or (at your option) any later version.
+
+ The GNU C 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 the GNU C Library; if not, see
+ . */
+
+/* Define this to have a standalone program to test this implementation of
+ mktime. */
+/* #define DEBUG 1 */
+
+#ifndef _LIBC
+# include
+#endif
+
+/* Assume that leap seconds are possible, unless told otherwise.
+ If the host has a 'zic' command with a '-L leapsecondfilename' option,
+ then it supports leap seconds; otherwise it probably doesn't. */
+#ifndef LEAP_SECONDS_POSSIBLE
+# define LEAP_SECONDS_POSSIBLE 1
+#endif
+
+#include
+
+#include
+
+#include /* For the real memcpy prototype. */
+
+#if defined DEBUG && DEBUG
+# include
+# include
+/* Make it work even if the system's libc has its own mktime routine. */
+# undef mktime
+# define mktime my_mktime
+#endif /* DEBUG */
+
+/* Some of the code in this file assumes that signed integer overflow
+ silently wraps around. This assumption can't easily be programmed
+ around, nor can it be checked for portably at compile-time or
+ easily eliminated at run-time.
+
+ Define WRAPV to 1 if the assumption is valid and if
+ #pragma GCC optimize ("wrapv")
+ does not trigger GCC bug 51793
+ .
+ Otherwise, define it to 0; this forces the use of slower code that,
+ while not guaranteed by the C Standard, works on all production
+ platforms that we know about. */
+#ifndef WRAPV
+# if (((__GNUC__ == 4 && 4 <= __GNUC_MINOR__) || 4 < __GNUC__) \
+ && defined __GLIBC__)
+# pragma GCC optimize ("wrapv")
+# define WRAPV 1
+# else
+# define WRAPV 0
+# endif
+#endif
+
+/* Verify a requirement at compile-time (unlike assert, which is runtime). */
+#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
+
+/* A signed type that is at least one bit wider than int. */
+#if INT_MAX <= LONG_MAX / 2
+typedef long int long_int;
+#else
+typedef long long int long_int;
+#endif
+verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2);
+
+/* Shift A right by B bits portably, by dividing A by 2**B and
+ truncating towards minus infinity. A and B should be free of side
+ effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
+ INT_BITS is the number of useful bits in an int. GNU code can
+ assume that INT_BITS is at least 32.
+
+ ISO C99 says that A >> B is implementation-defined if A < 0. Some
+ implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
+ right in the usual way when A < 0, so SHR falls back on division if
+ ordinary A >> B doesn't seem to be the usual signed shift. */
+#define SHR(a, b) \
+ ((-1 >> 1 == -1 \
+ && (long_int) -1 >> 1 == -1 \
+ && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t))) \
+ ? (a) >> (b) \
+ : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
+
+/* The extra casts in the following macros work around compiler bugs,
+ e.g., in Cray C 5.0.3.0. */
+
+/* True if the arithmetic type T is an integer type. bool counts as
+ an integer. */
+#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
+
+/* True if negative values of the signed integer type T use two's
+ complement, or if T is an unsigned integer type. */
+#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
+
+/* True if the arithmetic type T is signed. */
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+/* The maximum and minimum values for the integer type T. These
+ macros have undefined behavior if T is signed and has padding bits.
+ If this is a problem for you, please let us know how to fix it for
+ your host. */
+#define TYPE_MINIMUM(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) 0 \
+ : ~ TYPE_MAXIMUM (t)))
+#define TYPE_MAXIMUM(t) \
+ ((t) (! TYPE_SIGNED (t) \
+ ? (t) -1 \
+ : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1)))
+
+#ifndef TIME_T_MIN
+# define TIME_T_MIN TYPE_MINIMUM (time_t)
+#endif
+#ifndef TIME_T_MAX
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
+#endif
+#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
+
+verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
+verify (twos_complement_arithmetic,
+ (TYPE_TWOS_COMPLEMENT (int)
+ && TYPE_TWOS_COMPLEMENT (long_int)
+ && TYPE_TWOS_COMPLEMENT (time_t)));
+
+#define EPOCH_YEAR 1970
+#define TM_YEAR_BASE 1900
+verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0);
+
+/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */
+static int
+leapyear (long_int year)
+{
+ /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
+ Also, work even if YEAR is negative. */
+ return
+ ((year & 3) == 0
+ && (year % 100 != 0
+ || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
+}
+
+/* How many days come before each month (0-12). */
+#ifndef _LIBC
+static
+#endif
+const unsigned short int __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+
+
+#ifndef _LIBC
+/* Portable standalone applications should supply a that
+ declares a POSIX-compliant localtime_r, for the benefit of older
+ implementations that lack localtime_r or have a nonstandard one.
+ See the gnulib time_r module for one way to implement this. */
+# undef __localtime_r
+# define __localtime_r localtime_r
+# define __mktime_internal mktime_internal
+# include "mktime-internal.h"
+#endif
+
+/* Return 1 if the values A and B differ according to the rules for
+ tm_isdst: A and B differ if one is zero and the other positive. */
+static int
+isdst_differ (int a, int b)
+{
+ return (!a != !b) && (0 <= a) && (0 <= b);
+}
+
+/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
+ (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
+ were not adjusted between the time stamps.
+
+ The YEAR values uses the same numbering as TP->tm_year. Values
+ need not be in the usual range. However, YEAR1 must not be less
+ than 2 * INT_MIN or greater than 2 * INT_MAX.
+
+ The result may overflow. It is the caller's responsibility to
+ detect overflow. */
+
+static time_t
+ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1,
+ int year0, int yday0, int hour0, int min0, int sec0)
+{
+ verify (C99_integer_division, -1 / 2 == 0);
+
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid integer overflow here. */
+ int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
+ int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = SHR (a100, 2);
+ int b400 = SHR (b100, 2);
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+
+ /* Compute the desired time in time_t precision. Overflow might
+ occur here. */
+ time_t tyear1 = year1;
+ time_t years = tyear1 - year0;
+ time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
+ time_t hours = 24 * days + hour1 - hour0;
+ time_t minutes = 60 * hours + min1 - min0;
+ time_t seconds = 60 * minutes + sec1 - sec0;
+ return seconds;
+}
+
+/* Return the average of A and B, even if A + B would overflow. */
+static time_t
+time_t_avg (time_t a, time_t b)
+{
+ return SHR (a, 1) + SHR (b, 1) + (a & b & 1);
+}
+
+/* Return 1 if A + B does not overflow. If time_t is unsigned and if
+ B's top bit is set, assume that the sum represents A - -B, and
+ return 1 if the subtraction does not wrap around. */
+static int
+time_t_add_ok (time_t a, time_t b)
+{
+ if (! TYPE_SIGNED (time_t))
+ {
+ time_t sum = a + b;
+ return (sum < a) == (TIME_T_MIDPOINT <= b);
+ }
+ else if (WRAPV)
+ {
+ time_t sum = a + b;
+ return (sum < a) == (b < 0);
+ }
+ else
+ {
+ time_t avg = time_t_avg (a, b);
+ return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
+ }
+}
+
+/* Return 1 if A + B does not overflow. */
+static int
+time_t_int_add_ok (time_t a, int b)
+{
+ verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX);
+ if (WRAPV)
+ {
+ time_t sum = a + b;
+ return (sum < a) == (b < 0);
+ }
+ else
+ {
+ int a_odd = a & 1;
+ time_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b));
+ return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2;
+ }
+}
+
+/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
+ assuming that *T corresponds to *TP and that no clock adjustments
+ occurred between *TP and the desired time.
+ If TP is null, return a value not equal to *T; this avoids false matches.
+ If overflow occurs, yield the minimal or maximal value, except do not
+ yield a value equal to *T. */
+static time_t
+guess_time_tm (long_int year, long_int yday, int hour, int min, int sec,
+ const time_t *t, const struct tm *tp)
+{
+ if (tp)
+ {
+ time_t d = ydhms_diff (year, yday, hour, min, sec,
+ tp->tm_year, tp->tm_yday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ if (time_t_add_ok (*t, d))
+ return *t + d;
+ }
+
+ /* Overflow occurred one way or another. Return the nearest result
+ that is actually in range, except don't report a zero difference
+ if the actual difference is nonzero, as that would cause a false
+ match; and don't oscillate between two values, as that would
+ confuse the spring-forward gap detector. */
+ return (*t < TIME_T_MIDPOINT
+ ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
+ : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
+}
+
+/* Use CONVERT to convert *T to a broken down time in *TP.
+ If *T is out of range for conversion, adjust it so that
+ it is the nearest in-range value and then convert that. */
+static struct tm *
+ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
+ time_t *t, struct tm *tp)
+{
+ struct tm *r = convert (t, tp);
+
+ if (!r && *t)
+ {
+ time_t bad = *t;
+ time_t ok = 0;
+
+ /* BAD is a known unconvertible time_t, and OK is a known good one.
+ Use binary search to narrow the range between BAD and OK until
+ they differ by 1. */
+ while (bad != ok + (bad < 0 ? -1 : 1))
+ {
+ time_t mid = *t = time_t_avg (ok, bad);
+ r = convert (t, tp);
+ if (r)
+ ok = mid;
+ else
+ bad = mid;
+ }
+
+ if (!r && ok)
+ {
+ /* The last conversion attempt failed;
+ revert to the most recent successful attempt. */
+ *t = ok;
+ r = convert (t, tp);
+ }
+ }
+
+ return r;
+}
+
+
+/* Convert *TP to a time_t value, inverting
+ the monotonic and mostly-unit-linear conversion function CONVERT.
+ Use *OFFSET to keep track of a guess at the offset of the result,
+ compared to what the result would be for UTC without leap seconds.
+ If *OFFSET's guess is correct, only one CONVERT call is needed.
+ This function is external because it is used also by timegm.c. */
+time_t
+__mktime_internal (struct tm *tp,
+ struct tm *(*convert) (const time_t *, struct tm *),
+ time_t *offset)
+{
+ time_t t, gt, t0, t1, t2;
+ struct tm tm;
+
+ /* The maximum number of probes (calls to CONVERT) should be enough
+ to handle any combinations of time zone rule changes, solar time,
+ leap seconds, and oscillations around a spring-forward gap.
+ POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
+ int remaining_probes = 6;
+
+ /* Time requested. Copy it in case CONVERT modifies *TP; this can
+ occur if TP is localtime's returned value and CONVERT is localtime. */
+ int sec = tp->tm_sec;
+ int min = tp->tm_min;
+ int hour = tp->tm_hour;
+ int mday = tp->tm_mday;
+ int mon = tp->tm_mon;
+ int year_requested = tp->tm_year;
+ int isdst = tp->tm_isdst;
+
+ /* 1 if the previous probe was DST. */
+ int dst2;
+
+ /* Ensure that mon is in range, and set year accordingly. */
+ int mon_remainder = mon % 12;
+ int negative_mon_remainder = mon_remainder < 0;
+ int mon_years = mon / 12 - negative_mon_remainder;
+ long_int lyear_requested = year_requested;
+ long_int year = lyear_requested + mon_years;
+
+ /* The other values need not be in range:
+ the remaining code handles minor overflows correctly,
+ assuming int and time_t arithmetic wraps around.
+ Major overflows are caught at the end. */
+
+ /* Calculate day of year from year, month, and day of month.
+ The result need not be in range. */
+ int mon_yday = ((__mon_yday[leapyear (year)]
+ [mon_remainder + 12 * negative_mon_remainder])
+ - 1);
+ long_int lmday = mday;
+ long_int yday = mon_yday + lmday;
+
+ time_t guessed_offset = *offset;
+
+ int sec_requested = sec;
+
+ if (LEAP_SECONDS_POSSIBLE)
+ {
+ /* Handle out-of-range seconds specially,
+ since ydhms_tm_diff assumes every minute has 60 seconds. */
+ if (sec < 0)
+ sec = 0;
+ if (59 < sec)
+ sec = 59;
+ }
+
+ /* Invert CONVERT by probing. First assume the same offset as last
+ time. */
+
+ t0 = ydhms_diff (year, yday, hour, min, sec,
+ EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
+
+ if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
+ {
+ /* time_t isn't large enough to rule out overflows, so check
+ for major overflows. A gross check suffices, since if t0
+ has overflowed, it is off by a multiple of TIME_T_MAX -
+ TIME_T_MIN + 1. So ignore any component of the difference
+ that is bounded by a small value. */
+
+ /* Approximate log base 2 of the number of time units per
+ biennium. A biennium is 2 years; use this unit instead of
+ years to avoid integer overflow. For example, 2 average
+ Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
+ which is 63113904 seconds, and rint (log2 (63113904)) is
+ 26. */
+ int ALOG2_SECONDS_PER_BIENNIUM = 26;
+ int ALOG2_MINUTES_PER_BIENNIUM = 20;
+ int ALOG2_HOURS_PER_BIENNIUM = 14;
+ int ALOG2_DAYS_PER_BIENNIUM = 10;
+ int LOG2_YEARS_PER_BIENNIUM = 1;
+
+ int approx_requested_biennia =
+ (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
+ - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
+ + SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
+ + SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
+ + SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
+ + (LEAP_SECONDS_POSSIBLE
+ ? 0
+ : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
+
+ int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
+ int diff = approx_biennia - approx_requested_biennia;
+ int approx_abs_diff = diff < 0 ? -1 - diff : diff;
+
+ /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously
+ gives a positive value of 715827882. Setting a variable
+ first then doing math on it seems to work.
+ (ghazi@caip.rutgers.edu) */
+ time_t time_t_max = TIME_T_MAX;
+ time_t time_t_min = TIME_T_MIN;
+ time_t overflow_threshold =
+ (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
+
+ if (overflow_threshold < approx_abs_diff)
+ {
+ /* Overflow occurred. Try repairing it; this might work if
+ the time zone offset is enough to undo the overflow. */
+ time_t repaired_t0 = -1 - t0;
+ approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
+ diff = approx_biennia - approx_requested_biennia;
+ approx_abs_diff = diff < 0 ? -1 - diff : diff;
+ if (overflow_threshold < approx_abs_diff)
+ return -1;
+ guessed_offset += repaired_t0 - t0;
+ t0 = repaired_t0;
+ }
+ }
+
+ /* Repeatedly use the error to improve the guess. */
+
+ for (t = t1 = t2 = t0, dst2 = 0;
+ (gt = guess_time_tm (year, yday, hour, min, sec, &t,
+ ranged_convert (convert, &t, &tm)),
+ t != gt);
+ t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)
+ if (t == t1 && t != t2
+ && (tm.tm_isdst < 0
+ || (isdst < 0
+ ? dst2 <= (tm.tm_isdst != 0)
+ : (isdst != 0) != (tm.tm_isdst != 0))))
+ /* We can't possibly find a match, as we are oscillating
+ between two values. The requested time probably falls
+ within a spring-forward gap of size GT - T. Follow the common
+ practice in this case, which is to return a time that is GT - T
+ away from the requested time, preferring a time whose
+ tm_isdst differs from the requested value. (If no tm_isdst
+ was requested and only one of the two values has a nonzero
+ tm_isdst, prefer that value.) In practice, this is more
+ useful than returning -1. */
+ goto offset_found;
+ else if (--remaining_probes == 0)
+ return -1;
+
+ /* We have a match. Check whether tm.tm_isdst has the requested
+ value, if any. */
+ if (isdst_differ (isdst, tm.tm_isdst))
+ {
+ /* tm.tm_isdst has the wrong value. Look for a neighboring
+ time with the right value, and use its UTC offset.
+
+ Heuristic: probe the adjacent timestamps in both directions,
+ looking for the desired isdst. This should work for all real
+ time zone histories in the tz database. */
+
+ /* Distance between probes when looking for a DST boundary. In
+ tzdata2003a, the shortest period of DST is 601200 seconds
+ (e.g., America/Recife starting 2000-10-08 01:00), and the
+ shortest period of non-DST surrounded by DST is 694800
+ seconds (Africa/Tunis starting 1943-04-17 01:00). Use the
+ minimum of these two values, so we don't miss these short
+ periods when probing. */
+ int stride = 601200;
+
+ /* The longest period of DST in tzdata2003a is 536454000 seconds
+ (e.g., America/Jujuy starting 1946-10-01 01:00). The longest
+ period of non-DST is much longer, but it makes no real sense
+ to search for more than a year of non-DST, so use the DST
+ max. */
+ int duration_max = 536454000;
+
+ /* Search in both directions, so the maximum distance is half
+ the duration; add the stride to avoid off-by-1 problems. */
+ int delta_bound = duration_max / 2 + stride;
+
+ int delta, direction;
+
+ for (delta = stride; delta < delta_bound; delta += stride)
+ for (direction = -1; direction <= 1; direction += 2)
+ if (time_t_int_add_ok (t, delta * direction))
+ {
+ time_t ot = t + delta * direction;
+ struct tm otm;
+ ranged_convert (convert, &ot, &otm);
+ if (! isdst_differ (isdst, otm.tm_isdst))
+ {
+ /* We found the desired tm_isdst.
+ Extrapolate back to the desired time. */
+ t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);
+ ranged_convert (convert, &t, &tm);
+ goto offset_found;
+ }
+ }
+ }
+
+ offset_found:
+ *offset = guessed_offset + t - t0;
+
+ if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
+ {
+ /* Adjust time to reflect the tm_sec requested, not the normalized value.
+ Also, repair any damage from a false match due to a leap second. */
+ int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
+ if (! time_t_int_add_ok (t, sec_requested))
+ return -1;
+ t1 = t + sec_requested;
+ if (! time_t_int_add_ok (t1, sec_adjustment))
+ return -1;
+ t2 = t1 + sec_adjustment;
+ if (! convert (&t2, &tm))
+ return -1;
+ t = t2;
+ }
+
+ *tp = tm;
+ return t;
+}
+
+
+/* FIXME: This should use a signed type wide enough to hold any UTC
+ offset in seconds. 'int' should be good enough for GNU code. We
+ can't fix this unilaterally though, as other modules invoke
+ __mktime_internal. */
+static time_t localtime_offset;
+
+/* Convert *TP to a time_t value. */
+time_t
+mktime (struct tm *tp)
+{
+#ifdef _LIBC
+ /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
+ time zone names contained in the external variable 'tzname' shall
+ be set as if the tzset() function had been called. */
+ __tzset ();
+#endif
+
+ return __mktime_internal (tp, __localtime_r, &localtime_offset);
+}
+
+#ifdef weak_alias
+weak_alias (mktime, timelocal)
+#endif
+
+#ifdef _LIBC
+libc_hidden_def (mktime)
+libc_hidden_weak (timelocal)
+#endif
+
+#if defined DEBUG && DEBUG
+
+static int
+not_equal_tm (const struct tm *a, const struct tm *b)
+{
+ return ((a->tm_sec ^ b->tm_sec)
+ | (a->tm_min ^ b->tm_min)
+ | (a->tm_hour ^ b->tm_hour)
+ | (a->tm_mday ^ b->tm_mday)
+ | (a->tm_mon ^ b->tm_mon)
+ | (a->tm_year ^ b->tm_year)
+ | (a->tm_yday ^ b->tm_yday)
+ | isdst_differ (a->tm_isdst, b->tm_isdst));
+}
+
+static void
+print_tm (const struct tm *tp)
+{
+ if (tp)
+ printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
+ tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec,
+ tp->tm_yday, tp->tm_wday, tp->tm_isdst);
+ else
+ printf ("0");
+}
+
+static int
+check_result (time_t tk, struct tm tmk, time_t tl, const struct tm *lt)
+{
+ if (tk != tl || !lt || not_equal_tm (&tmk, lt))
+ {
+ printf ("mktime (");
+ print_tm (lt);
+ printf (")\nyields (");
+ print_tm (&tmk);
+ printf (") == %ld, should be %ld\n", (long int) tk, (long int) tl);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ int status = 0;
+ struct tm tm, tmk, tml;
+ struct tm *lt;
+ time_t tk, tl, tl1;
+ char trailer;
+
+ if ((argc == 3 || argc == 4)
+ && (sscanf (argv[1], "%d-%d-%d%c",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
+ == 3)
+ && (sscanf (argv[2], "%d:%d:%d%c",
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
+ == 3))
+ {
+ tm.tm_year -= TM_YEAR_BASE;
+ tm.tm_mon--;
+ tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
+ tmk = tm;
+ tl = mktime (&tmk);
+ lt = localtime (&tl);
+ if (lt)
+ {
+ tml = *lt;
+ lt = &tml;
+ }
+ printf ("mktime returns %ld == ", (long int) tl);
+ print_tm (&tmk);
+ printf ("\n");
+ status = check_result (tl, tmk, tl, lt);
+ }
+ else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
+ {
+ time_t from = atol (argv[1]);
+ time_t by = atol (argv[2]);
+ time_t to = atol (argv[3]);
+
+ if (argc == 4)
+ for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
+ {
+ lt = localtime (&tl);
+ if (lt)
+ {
+ tmk = tml = *lt;
+ tk = mktime (&tmk);
+ status |= check_result (tk, tmk, tl, &tml);
+ }
+ else
+ {
+ printf ("localtime (%ld) yields 0\n", (long int) tl);
+ status = 1;
+ }
+ tl1 = tl + by;
+ if ((tl1 < tl) != (by < 0))
+ break;
+ }
+ else
+ for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1)
+ {
+ /* Null benchmark. */
+ lt = localtime (&tl);
+ if (lt)
+ {
+ tmk = tml = *lt;
+ tk = tl;
+ status |= check_result (tk, tmk, tl, &tml);
+ }
+ else
+ {
+ printf ("localtime (%ld) yields 0\n", (long int) tl);
+ status = 1;
+ }
+ tl1 = tl + by;
+ if ((tl1 < tl) != (by < 0))
+ break;
+ }
+ }
+ else
+ printf ("Usage:\
+\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
+\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
+\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
+ argv[0], argv[0], argv[0]);
+
+ return status;
+}
+
+#endif /* DEBUG */
+
+/*
+Local Variables:
+compile-command: "gcc -DDEBUG -I. -Wall -W -O2 -g mktime.c -o mktime"
+End:
+*/
diff --git a/lib/msvc-inval.c b/lib/msvc-inval.c
index 84190d097..7ee21e437 100644
--- a/lib/msvc-inval.c
+++ b/lib/msvc-inval.c
@@ -1,5 +1,5 @@
/* Invalid parameter handler for MSVC runtime libraries.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/msvc-inval.h b/lib/msvc-inval.h
index c6df57e93..c73511df2 100644
--- a/lib/msvc-inval.h
+++ b/lib/msvc-inval.h
@@ -1,5 +1,5 @@
/* Invalid parameter handler for MSVC runtime libraries.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/msvc-nothrow.c b/lib/msvc-nothrow.c
index 9b1eb598e..868388183 100644
--- a/lib/msvc-nothrow.c
+++ b/lib/msvc-nothrow.c
@@ -1,6 +1,6 @@
/* Wrappers that don't throw invalid parameter notifications
with MSVC runtime libraries.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/msvc-nothrow.h b/lib/msvc-nothrow.h
index 1917325b3..f596aaf45 100644
--- a/lib/msvc-nothrow.h
+++ b/lib/msvc-nothrow.h
@@ -1,6 +1,6 @@
/* Wrappers that don't throw invalid parameter notifications
with MSVC runtime libraries.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/netdb.in.h b/lib/netdb.in.h
index 3613fb5a5..530a5335a 100644
--- a/lib/netdb.in.h
+++ b/lib/netdb.in.h
@@ -1,5 +1,5 @@
/* Provide a netdb.h header file for systems lacking it (read: MinGW).
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
Written by Simon Josefsson.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/netinet_in.in.h b/lib/netinet_in.in.h
index 8ab66a1df..e4709bbaf 100644
--- a/lib/netinet_in.in.h
+++ b/lib/netinet_in.in.h
@@ -1,5 +1,5 @@
/* Substitute for .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/nl_langinfo.c b/lib/nl_langinfo.c
index 83d2c77af..ff0adc4b9 100644
--- a/lib/nl_langinfo.c
+++ b/lib/nl_langinfo.c
@@ -1,6 +1,6 @@
/* nl_langinfo() replacement: query locale dependent information.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -20,13 +20,72 @@
/* Specification. */
#include
+#include
+#include
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN /* avoid including junk */
+# include
+# include
+#endif
+
+/* Return the codeset of the current locale, if this is easily deducible.
+ Otherwise, return "". */
+static char *
+ctype_codeset (void)
+{
+ static char buf[2 + 10 + 1];
+ size_t buflen = 0;
+ char const *locale = setlocale (LC_CTYPE, NULL);
+ char *codeset = buf;
+ size_t codesetlen;
+ codeset[0] = '\0';
+
+ if (locale && locale[0])
+ {
+ /* If the locale name contains an encoding after the dot, return it. */
+ char *dot = strchr (locale, '.');
+
+ if (dot)
+ {
+ /* Look for the possible @... trailer and remove it, if any. */
+ char *codeset_start = dot + 1;
+ char const *modifier = strchr (codeset_start, '@');
+
+ if (! modifier)
+ codeset = codeset_start;
+ else
+ {
+ codesetlen = modifier - codeset_start;
+ if (codesetlen < sizeof buf)
+ {
+ codeset = memcpy (buf, codeset_start, codesetlen);
+ codeset[codesetlen] = '\0';
+ }
+ }
+ }
+ }
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+ /* If setlocale is successful, it returns the number of the
+ codepage, as a string. Otherwise, fall back on Windows API
+ GetACP, which returns the locale's codepage as a number (although
+ this doesn't change according to what the 'setlocale' call specified).
+ Either way, prepend "CP" to make it a valid codeset name. */
+ codesetlen = strlen (codeset);
+ if (0 < codesetlen && codesetlen < sizeof buf - 2)
+ memmove (buf + 2, codeset, codesetlen + 1);
+ else
+ sprintf (buf + 2, "%u", GetACP ());
+ codeset = memcpy (buf, "CP", 2);
+#endif
+ return codeset;
+}
+
+
#if REPLACE_NL_LANGINFO
/* Override nl_langinfo with support for added nl_item values. */
-# include
-# include
-
# undef nl_langinfo
char *
@@ -36,36 +95,7 @@ rpl_nl_langinfo (nl_item item)
{
# if GNULIB_defined_CODESET
case CODESET:
- {
- const char *locale;
- static char buf[2 + 10 + 1];
-
- locale = setlocale (LC_CTYPE, NULL);
- if (locale != NULL && locale[0] != '\0')
- {
- /* If the locale name contains an encoding after the dot, return
- it. */
- const char *dot = strchr (locale, '.');
-
- if (dot != NULL)
- {
- const char *modifier;
-
- dot++;
- /* Look for the possible @... trailer and remove it, if any. */
- modifier = strchr (dot, '@');
- if (modifier == NULL)
- return dot;
- if (modifier - dot < sizeof (buf))
- {
- memcpy (buf, dot, modifier - dot);
- buf [modifier - dot] = '\0';
- return buf;
- }
- }
- }
- return "";
- }
+ return ctype_codeset ();
# endif
# if GNULIB_defined_T_FMT_AMPM
case T_FMT_AMPM:
@@ -111,42 +141,28 @@ rpl_nl_langinfo (nl_item item)
#else
-/* Provide nl_langinfo from scratch. */
+/* Provide nl_langinfo from scratch, either for native MS-Windows, or
+ for old Unix platforms without locales, such as Linux libc5 or
+ BeOS. */
-# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
-
-/* Native Windows platforms. */
-
-# define WIN32_LEAN_AND_MEAN /* avoid including junk */
-# include
-
-# include
-
-# else
-
-/* An old Unix platform without locales, such as Linux libc5 or BeOS. */
-
-# endif
-
-# include
+# include
char *
nl_langinfo (nl_item item)
{
+ static char nlbuf[100];
+ struct tm tmm = { 0 };
+
switch (item)
{
/* nl_langinfo items of the LC_CTYPE category */
case CODESET:
-# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
{
- static char buf[2 + 10 + 1];
-
- /* The Windows API has a function returning the locale's codepage as
- a number. */
- sprintf (buf, "CP%u", GetACP ());
- return buf;
+ char *codeset = ctype_codeset ();
+ if (*codeset)
+ return codeset;
}
-# elif defined __BEOS__
+# ifdef __BEOS__
return "UTF-8";
# else
return "ISO-8859-1";
@@ -156,6 +172,8 @@ nl_langinfo (nl_item item)
return localeconv () ->decimal_point;
case THOUSEP:
return localeconv () ->thousands_sep;
+ case GROUPING:
+ return localeconv () ->grouping;
/* nl_langinfo items of the LC_TIME category.
TODO: Really use the locale. */
case D_T_FMT:
@@ -170,93 +188,126 @@ nl_langinfo (nl_item item)
case T_FMT_AMPM:
return "%I:%M:%S %p";
case AM_STR:
- return "AM";
+ if (!strftime (nlbuf, sizeof nlbuf, "%p", &tmm))
+ return "AM";
+ return nlbuf;
case PM_STR:
- return "PM";
+ tmm.tm_hour = 12;
+ if (!strftime (nlbuf, sizeof nlbuf, "%p", &tmm))
+ return "PM";
+ return nlbuf;
case DAY_1:
- return "Sunday";
case DAY_2:
- return "Monday";
case DAY_3:
- return "Tuesday";
case DAY_4:
- return "Wednesday";
case DAY_5:
- return "Thursday";
case DAY_6:
- return "Friday";
case DAY_7:
- return "Saturday";
+ {
+ static char const days[][sizeof "Wednesday"] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday"
+ };
+ tmm.tm_wday = item - DAY_1;
+ if (!strftime (nlbuf, sizeof nlbuf, "%A", &tmm))
+ return (char *) days[item - DAY_1];
+ return nlbuf;
+ }
case ABDAY_1:
- return "Sun";
case ABDAY_2:
- return "Mon";
case ABDAY_3:
- return "Tue";
case ABDAY_4:
- return "Wed";
case ABDAY_5:
- return "Thu";
case ABDAY_6:
- return "Fri";
case ABDAY_7:
- return "Sat";
+ {
+ static char const abdays[][sizeof "Sun"] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+ tmm.tm_wday = item - ABDAY_1;
+ if (!strftime (nlbuf, sizeof nlbuf, "%a", &tmm))
+ return (char *) abdays[item - ABDAY_1];
+ return nlbuf;
+ }
case MON_1:
- return "January";
case MON_2:
- return "February";
case MON_3:
- return "March";
case MON_4:
- return "April";
case MON_5:
- return "May";
case MON_6:
- return "June";
case MON_7:
- return "July";
case MON_8:
- return "August";
case MON_9:
- return "September";
case MON_10:
- return "October";
case MON_11:
- return "November";
case MON_12:
- return "December";
+ {
+ static char const months[][sizeof "September"] = {
+ "January", "February", "March", "April", "May", "June", "July",
+ "September", "October", "November", "December"
+ };
+ tmm.tm_mon = item - MON_1;
+ if (!strftime (nlbuf, sizeof nlbuf, "%B", &tmm))
+ return (char *) months[item - MON_1];
+ return nlbuf;
+ }
case ABMON_1:
- return "Jan";
case ABMON_2:
- return "Feb";
case ABMON_3:
- return "Mar";
case ABMON_4:
- return "Apr";
case ABMON_5:
- return "May";
case ABMON_6:
- return "Jun";
case ABMON_7:
- return "Jul";
case ABMON_8:
- return "Aug";
case ABMON_9:
- return "Sep";
case ABMON_10:
- return "Oct";
case ABMON_11:
- return "Nov";
case ABMON_12:
- return "Dec";
+ {
+ static char const abmonths[][sizeof "Jan"] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+ "Sep", "Oct", "Nov", "Dec"
+ };
+ tmm.tm_mon = item - ABMON_1;
+ if (!strftime (nlbuf, sizeof nlbuf, "%b", &tmm))
+ return (char *) abmonths[item - ABMON_1];
+ return nlbuf;
+ }
case ERA:
return "";
case ALT_DIGITS:
return "\0\0\0\0\0\0\0\0\0\0";
- /* nl_langinfo items of the LC_MONETARY category
- TODO: Really use the locale. */
+ /* nl_langinfo items of the LC_MONETARY category. */
case CRNCYSTR:
- return "-";
+ return localeconv () ->currency_symbol;
+ case INT_CURR_SYMBOL:
+ return localeconv () ->int_curr_symbol;
+ case MON_DECIMAL_POINT:
+ return localeconv () ->mon_decimal_point;
+ case MON_THOUSANDS_SEP:
+ return localeconv () ->mon_thousands_sep;
+ case MON_GROUPING:
+ return localeconv () ->mon_grouping;
+ case POSITIVE_SIGN:
+ return localeconv () ->positive_sign;
+ case NEGATIVE_SIGN:
+ return localeconv () ->negative_sign;
+ case FRAC_DIGITS:
+ return & localeconv () ->frac_digits;
+ case INT_FRAC_DIGITS:
+ return & localeconv () ->int_frac_digits;
+ case P_CS_PRECEDES:
+ return & localeconv () ->p_cs_precedes;
+ case N_CS_PRECEDES:
+ return & localeconv () ->n_cs_precedes;
+ case P_SEP_BY_SPACE:
+ return & localeconv () ->p_sep_by_space;
+ case N_SEP_BY_SPACE:
+ return & localeconv () ->n_sep_by_space;
+ case P_SIGN_POSN:
+ return & localeconv () ->p_sign_posn;
+ case N_SIGN_POSN:
+ return & localeconv () ->n_sign_posn;
/* nl_langinfo items of the LC_MESSAGES category
TODO: Really use the locale. */
case YESEXPR:
diff --git a/lib/nproc.c b/lib/nproc.c
index 293c65169..b1c25b138 100644
--- a/lib/nproc.c
+++ b/lib/nproc.c
@@ -1,6 +1,6 @@
/* Detect the number of processors.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/nproc.h b/lib/nproc.h
index dbc315707..4c49777aa 100644
--- a/lib/nproc.h
+++ b/lib/nproc.h
@@ -1,6 +1,6 @@
/* Detect the number of processors.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/open.c b/lib/open.c
index f6fd06e4c..4345943d2 100644
--- a/lib/open.c
+++ b/lib/open.c
@@ -1,5 +1,5 @@
/* Open a descriptor to a file.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/pathmax.h b/lib/pathmax.h
index 15ed6c28e..c4c94528f 100644
--- a/lib/pathmax.h
+++ b/lib/pathmax.h
@@ -1,5 +1,5 @@
/* Define PATH_MAX somehow. Requires sys/types.h.
- Copyright (C) 1992, 1999, 2001, 2003, 2005, 2009-2014 Free Software
+ Copyright (C) 1992, 1999, 2001, 2003, 2005, 2009-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/pipe.c b/lib/pipe.c
index 03aed5ef9..d733ed068 100644
--- a/lib/pipe.c
+++ b/lib/pipe.c
@@ -1,5 +1,5 @@
/* Create a pipe.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/pipe2.c b/lib/pipe2.c
index 4e4e894e7..94a79e219 100644
--- a/lib/pipe2.c
+++ b/lib/pipe2.c
@@ -1,5 +1,5 @@
/* Create a pipe, with specific opening flags.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/poll.c b/lib/poll.c
index 7b1e58266..e70e8b354 100644
--- a/lib/poll.c
+++ b/lib/poll.c
@@ -1,7 +1,7 @@
/* Emulation for poll(2)
Contributed by Paolo Bonzini.
- Copyright 2001-2003, 2006-2014 Free Software Foundation, Inc.
+ Copyright 2001-2003, 2006-2015 Free Software Foundation, Inc.
This file is part of gnulib.
@@ -33,7 +33,6 @@
#include
#include
-#include
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
# define WINDOWS_NATIVE
@@ -45,11 +44,12 @@
# include "msvc-nothrow.h"
#else
# include
-# include
-# include
# include
#endif
+#include
+#include
+
#ifdef HAVE_SYS_IOCTL_H
# include
#endif
@@ -59,6 +59,8 @@
#include
+#include "assure.h"
+
#ifndef INFTIM
# define INFTIM (-1)
#endif
@@ -70,9 +72,11 @@
#ifdef WINDOWS_NATIVE
-/* Optimized test whether a HANDLE refers to a console.
- See . */
-#define IsConsoleHandle(h) (((intptr_t) (h) & 3) == 3)
+static BOOL IsConsoleHandle (HANDLE h)
+{
+ DWORD mode;
+ return GetConsoleMode (h, &mode) != 0;
+}
static BOOL
IsSocketHandle (HANDLE h)
@@ -331,26 +335,15 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
int maxfd, rc;
nfds_t i;
-# ifdef _SC_OPEN_MAX
- static int sc_open_max = -1;
-
- if (nfd < 0
- || (nfd > sc_open_max
- && (sc_open_max != -1
- || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX)))))
+ if (nfd < 0)
{
errno = EINVAL;
return -1;
}
-# else /* !_SC_OPEN_MAX */
-# ifdef OPEN_MAX
- if (nfd < 0 || nfd > OPEN_MAX)
- {
- errno = EINVAL;
- return -1;
- }
-# endif /* OPEN_MAX -- else, no check is needed */
-# endif /* !_SC_OPEN_MAX */
+ /* Don't check directly for NFD too large. Any practical use of a
+ too-large NFD is caught by one of the other checks below, and
+ checking directly for getdtablesize is too much of a portability
+ and/or performance and/or correctness hassle. */
/* EFAULT is not necessary to implement, but let's do it in the
simplest case. */
@@ -391,10 +384,17 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
{
if (pfd[i].fd < 0)
continue;
-
+ if (maxfd < pfd[i].fd)
+ {
+ maxfd = pfd[i].fd;
+ if (FD_SETSIZE <= maxfd)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ }
if (pfd[i].events & (POLLIN | POLLRDNORM))
FD_SET (pfd[i].fd, &rfds);
-
/* see select(2): "the only exceptional condition detectable
is out-of-band data received on a socket", hence we push
POLLWRBAND events onto wfds instead of efds. */
@@ -402,18 +402,6 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
FD_SET (pfd[i].fd, &wfds);
if (pfd[i].events & (POLLPRI | POLLRDBAND))
FD_SET (pfd[i].fd, &efds);
- if (pfd[i].fd >= maxfd
- && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI
- | POLLRDNORM | POLLRDBAND
- | POLLWRNORM | POLLWRBAND)))
- {
- maxfd = pfd[i].fd;
- if (maxfd > FD_SETSIZE)
- {
- errno = EOVERFLOW;
- return -1;
- }
- }
}
/* examine fd sets */
@@ -424,18 +412,13 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
/* establish results */
rc = 0;
for (i = 0; i < nfd; i++)
- if (pfd[i].fd < 0)
- pfd[i].revents = 0;
- else
- {
- int happened = compute_revents (pfd[i].fd, pfd[i].events,
- &rfds, &wfds, &efds);
- if (happened)
- {
- pfd[i].revents = happened;
- rc++;
- }
- }
+ {
+ pfd[i].revents = (pfd[i].fd < 0
+ ? 0
+ : compute_revents (pfd[i].fd, pfd[i].events,
+ &rfds, &wfds, &efds));
+ rc += pfd[i].revents != 0;
+ }
return rc;
#else
@@ -478,7 +461,7 @@ restart:
continue;
h = (HANDLE) _get_osfhandle (pfd[i].fd);
- assert (h != NULL);
+ assure (h != NULL);
if (IsSocketHandle (h))
{
int requested = FD_CLOSE;
diff --git a/lib/poll.in.h b/lib/poll.in.h
index bde98064f..0a9950157 100644
--- a/lib/poll.in.h
+++ b/lib/poll.in.h
@@ -1,7 +1,7 @@
/* Header for poll(2) emulation
Contributed by Paolo Bonzini.
- Copyright 2001-2003, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright 2001-2003, 2007, 2009-2015 Free Software Foundation, Inc.
This file is part of gnulib.
diff --git a/lib/printf-args.c b/lib/printf-args.c
index 9673e6ddc..de7a6bf3f 100644
--- a/lib/printf-args.c
+++ b/lib/printf-args.c
@@ -1,5 +1,5 @@
/* Decomposed printf argument list.
- Copyright (C) 1999, 2002-2003, 2005-2007, 2009-2014 Free Software
+ Copyright (C) 1999, 2002-2003, 2005-2007, 2009-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/printf-args.h b/lib/printf-args.h
index 831c14738..a413b5270 100644
--- a/lib/printf-args.h
+++ b/lib/printf-args.h
@@ -1,5 +1,5 @@
/* Decomposed printf argument list.
- Copyright (C) 1999, 2002-2003, 2006-2007, 2011-2014 Free Software
+ Copyright (C) 1999, 2002-2003, 2006-2007, 2011-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/printf-parse.c b/lib/printf-parse.c
index e6a09a8de..b4592363b 100644
--- a/lib/printf-parse.c
+++ b/lib/printf-parse.c
@@ -1,5 +1,5 @@
/* Formatted output to strings.
- Copyright (C) 1999-2000, 2002-2003, 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2000, 2002-2003, 2006-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/printf-parse.h b/lib/printf-parse.h
index 44d6f5513..d35ffcaf1 100644
--- a/lib/printf-parse.h
+++ b/lib/printf-parse.h
@@ -1,5 +1,5 @@
/* Parse printf format string.
- Copyright (C) 1999, 2002-2003, 2005, 2007, 2010-2014 Free Software
+ Copyright (C) 1999, 2002-2003, 2005, 2007, 2010-2015 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/putenv.c b/lib/putenv.c
index de8caa712..54687629c 100644
--- a/lib/putenv.c
+++ b/lib/putenv.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2014 Free Software
+/* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2015 Free Software
Foundation, Inc.
NOTE: The canonical source of this file is maintained with the GNU C
diff --git a/lib/raise.c b/lib/raise.c
index 2f04eea9b..b099b58d1 100644
--- a/lib/raise.c
+++ b/lib/raise.c
@@ -1,6 +1,6 @@
/* Provide a non-threads replacement for the POSIX raise function.
- Copyright (C) 2002-2003, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/read.c b/lib/read.c
index 4efe8ce23..7edb99110 100644
--- a/lib/read.c
+++ b/lib/read.c
@@ -1,5 +1,5 @@
/* POSIX compatible read() function.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/readlink.c b/lib/readlink.c
index ef502f57b..d86f822aa 100644
--- a/lib/readlink.c
+++ b/lib/readlink.c
@@ -1,5 +1,5 @@
/* Stub for readlink().
- Copyright (C) 2003-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/recv.c b/lib/recv.c
index fc7e12406..edb7e01f1 100644
--- a/lib/recv.c
+++ b/lib/recv.c
@@ -1,6 +1,6 @@
/* recv.c --- wrappers for Windows recv function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/recvfrom.c b/lib/recvfrom.c
index 0d4fba076..4ecffb0e0 100644
--- a/lib/recvfrom.c
+++ b/lib/recvfrom.c
@@ -1,6 +1,6 @@
/* recvfrom.c --- wrappers for Windows recvfrom function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/ref-add.sin b/lib/ref-add.sin
index 9adfb0df0..4d406a37d 100644
--- a/lib/ref-add.sin
+++ b/lib/ref-add.sin
@@ -1,6 +1,6 @@
# Add this package to a list of references stored in a text file.
#
-# Copyright (C) 2000, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2000, 2009-2015 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/ref-del.sin b/lib/ref-del.sin
index 45449cbba..5b4ff6246 100644
--- a/lib/ref-del.sin
+++ b/lib/ref-del.sin
@@ -1,6 +1,6 @@
# Remove this package from a list of references stored in a text file.
#
-# Copyright (C) 2000, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2000, 2009-2015 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/regcomp.c b/lib/regcomp.c
index 56faf11c4..4cbb1b2b9 100644
--- a/lib/regcomp.c
+++ b/lib/regcomp.c
@@ -1,5 +1,5 @@
/* Extended regular expression matching and search library.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa .
@@ -17,6 +17,10 @@
License along with the GNU C Library; if not, see
. */
+#ifdef _LIBC
+# include
+#endif
+
static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
size_t length, reg_syntax_t syntax);
static void re_compile_fastmap_iter (regex_t *bufp,
@@ -335,7 +339,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
memset (&state, '\0', sizeof (state));
if (__mbrtowc (&wc, (const char *) buf, p - buf,
&state) == p - buf
- && (__wcrtomb ((char *) buf, towlower (wc), &state)
+ && (__wcrtomb ((char *) buf, __towlower (wc), &state)
!= (size_t) -1))
re_set_fastmap (fastmap, false, buf[0]);
}
@@ -411,7 +415,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
{
- if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+ if (__wcrtomb (buf, __towlower (cset->mbchars[i]), &state)
!= (size_t) -1)
re_set_fastmap (fastmap, false, *(unsigned char *) buf);
}
@@ -2187,6 +2191,7 @@ parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
{
re_dfa_t *dfa = preg->buffer;
bin_tree_t *tree, *branch = NULL;
+ bitset_word_t initial_bkref_map = dfa->completed_bkref_map;
tree = parse_branch (regexp, preg, token, syntax, nest, err);
if (BE (*err != REG_NOERROR && tree == NULL, 0))
return NULL;
@@ -2197,9 +2202,16 @@ parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
if (token->type != OP_ALT && token->type != END_OF_RE
&& (nest == 0 || token->type != OP_CLOSE_SUBEXP))
{
+ bitset_word_t accumulated_bkref_map = dfa->completed_bkref_map;
+ dfa->completed_bkref_map = initial_bkref_map;
branch = parse_branch (regexp, preg, token, syntax, nest, err);
if (BE (*err != REG_NOERROR && branch == NULL, 0))
- return NULL;
+ {
+ if (tree != NULL)
+ postorder (tree, free_tree, NULL);
+ return NULL;
+ }
+ dfa->completed_bkref_map |= accumulated_bkref_map;
}
else
branch = NULL;
@@ -2460,14 +2472,22 @@ parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
|| token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
{
- tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
- if (BE (*err != REG_NOERROR && tree == NULL, 0))
- return NULL;
+ bin_tree_t *dup_tree = parse_dup_op (tree, regexp, dfa, token,
+ syntax, err);
+ if (BE (*err != REG_NOERROR && dup_tree == NULL, 0))
+ {
+ if (tree != NULL)
+ postorder (tree, free_tree, NULL);
+ return NULL;
+ }
+ tree = dup_tree;
/* In BRE consecutive duplications are not allowed. */
if ((syntax & RE_CONTEXT_INVALID_DUP)
&& (token->type == OP_DUP_ASTERISK
|| token->type == OP_OPEN_DUP_NUM))
{
+ if (tree != NULL)
+ postorder (tree, free_tree, NULL);
*err = REG_BADRPT;
return NULL;
}
@@ -2623,6 +2643,8 @@ parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
/* Duplicate ELEM before it is marked optional. */
elem = duplicate_tree (elem, dfa);
+ if (BE (elem == NULL, 0))
+ goto parse_dup_op_espace;
old_tree = tree;
}
else
@@ -3161,6 +3183,7 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
re_token_t token2;
start_elem.opr.name = start_name_buf;
+ start_elem.type = COLL_SYM;
ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
syntax, first_round);
if (BE (ret != REG_NOERROR, 0))
@@ -3204,6 +3227,7 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
if (is_range_exp == true)
{
end_elem.opr.name = end_name_buf;
+ end_elem.type = COLL_SYM;
ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
dfa, syntax, true);
if (BE (ret != REG_NOERROR, 0))
@@ -3478,8 +3502,6 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
int32_t idx1, idx2;
unsigned int ch;
size_t len;
- /* This #include defines a local function! */
-# include
/* Calculate the index for equivalence class. */
cp = name;
table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
@@ -3489,7 +3511,7 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
_NL_COLLATE_EXTRAMB);
indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
_NL_COLLATE_INDIRECTMB);
- idx1 = findidx (&cp, -1);
+ idx1 = findidx (table, indirect, extra, &cp, -1);
if (BE (idx1 == 0 || *cp != '\0', 0))
/* This isn't a valid character. */
return REG_ECOLLATE;
@@ -3500,7 +3522,7 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
{
char_buf[0] = ch;
cp = char_buf;
- idx2 = findidx (&cp, 1);
+ idx2 = findidx (table, indirect, extra, &cp, 1);
/*
idx2 = table[ch];
*/
diff --git a/lib/regex.c b/lib/regex.c
index e44f55fd1..1adc8a8ae 100644
--- a/lib/regex.c
+++ b/lib/regex.c
@@ -1,5 +1,5 @@
/* Extended regular expression matching and search library.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa .
diff --git a/lib/regex.h b/lib/regex.h
index 54327c69e..6f3bae3ae 100644
--- a/lib/regex.h
+++ b/lib/regex.h
@@ -1,6 +1,6 @@
/* Definitions for data structures and routines for the regular
expression library.
- Copyright (C) 1985, 1989-1993, 1995-1998, 2000-2003, 2005-2014 Free Software
+ Copyright (C) 1985, 1989-1993, 1995-1998, 2000-2003, 2005-2015 Free Software
Foundation, Inc.
This file is part of the GNU C Library.
@@ -244,19 +244,16 @@ extern reg_syntax_t re_syntax_options;
| RE_INVALID_INTERVAL_ORD)
# define RE_SYNTAX_GREP \
- (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
- | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
- | RE_NEWLINE_ALT)
+ ((RE_SYNTAX_POSIX_BASIC | RE_NEWLINE_ALT) \
+ & ~(RE_CONTEXT_INVALID_DUP | RE_DOT_NOT_NULL))
# define RE_SYNTAX_EGREP \
- (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
- | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
- | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
- | RE_NO_BK_VBAR)
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_INVALID_INTERVAL_ORD | RE_NEWLINE_ALT) \
+ & ~(RE_CONTEXT_INVALID_OPS | RE_DOT_NOT_NULL))
+/* POSIX grep -E behavior is no longer incompatible with GNU. */
# define RE_SYNTAX_POSIX_EGREP \
- (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
- | RE_INVALID_INTERVAL_ORD)
+ RE_SYNTAX_EGREP
/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
@@ -608,7 +605,7 @@ extern void re_set_registers (struct re_pattern_buffer *__buffer,
regoff_t *__starts, regoff_t *__ends);
#endif /* Use GNU */
-#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_MISC)
# ifndef _CRAY
/* 4.2 bsd compatibility. */
extern char *re_comp (const char *);
diff --git a/lib/regex_internal.c b/lib/regex_internal.c
index 0343ee6e3..93d7ee964 100644
--- a/lib/regex_internal.c
+++ b/lib/regex_internal.c
@@ -1,5 +1,5 @@
/* Extended regular expression matching and search library.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa .
@@ -311,13 +311,12 @@ build_wcs_upper_buffer (re_string_t *pstr)
+ byte_idx), remain_len, &pstr->cur_state);
if (BE (mbclen < (size_t) -2, 1))
{
- wchar_t wcu = wc;
- if (iswlower (wc))
+ wchar_t wcu = __towupper (wc);
+ if (wcu != wc)
{
size_t mbcdlen;
- wcu = towupper (wc);
- mbcdlen = wcrtomb (buf, wcu, &prev_st);
+ mbcdlen = __wcrtomb (buf, wcu, &prev_st);
if (BE (mbclen == mbcdlen, 1))
memcpy (pstr->mbs + byte_idx, buf, mbclen);
else
@@ -381,12 +380,11 @@ build_wcs_upper_buffer (re_string_t *pstr)
mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
if (BE (mbclen < (size_t) -2, 1))
{
- wchar_t wcu = wc;
- if (iswlower (wc))
+ wchar_t wcu = __towupper (wc);
+ if (wcu != wc)
{
size_t mbcdlen;
- wcu = towupper (wc);
mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
if (BE (mbclen == mbcdlen, 1))
memcpy (pstr->mbs + byte_idx, buf, mbclen);
@@ -538,10 +536,7 @@ build_upper_buffer (re_string_t *pstr)
int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
if (BE (pstr->trans != NULL, 0))
ch = pstr->trans[ch];
- if (islower (ch))
- pstr->mbs[char_idx] = toupper (ch);
- else
- pstr->mbs[char_idx] = ch;
+ pstr->mbs[char_idx] = toupper (ch);
}
pstr->valid_len = char_idx;
pstr->valid_raw_len = char_idx;
@@ -682,7 +677,7 @@ re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags)
pstr->valid_len - offset);
pstr->valid_len -= offset;
pstr->valid_raw_len -= offset;
-#if DEBUG
+#if defined DEBUG && DEBUG
assert (pstr->valid_len > 0);
#endif
}
@@ -941,7 +936,7 @@ re_string_context_at (const re_string_t *input, Idx idx, int eflags)
Idx wc_idx = idx;
while(input->wcs[wc_idx] == WEOF)
{
-#ifdef DEBUG
+#if defined DEBUG && DEBUG
/* It must not happen. */
assert (REG_VALID_INDEX (wc_idx));
#endif
diff --git a/lib/regex_internal.h b/lib/regex_internal.h
index a0eae33e9..0307a340f 100644
--- a/lib/regex_internal.h
+++ b/lib/regex_internal.h
@@ -1,5 +1,5 @@
/* Extended regular expression matching and search library.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa .
@@ -34,13 +34,13 @@
#include
#ifdef _LIBC
-# include
+# include
# define lock_define(name) __libc_lock_define (, name)
# define lock_init(lock) (__libc_lock_init (lock), 0)
# define lock_fini(lock) 0
# define lock_lock(lock) __libc_lock_lock (lock)
# define lock_unlock(lock) __libc_lock_unlock (lock)
-#elif defined GNULIB_LOCK
+#elif defined GNULIB_LOCK && !defined USE_UNLOCKED_IO
# include "glthread/lock.h"
/* Use gl_lock_define if empty macro arguments are known to work.
Otherwise, fall back on less-portable substitutes. */
@@ -62,7 +62,7 @@
# define lock_fini(lock) glthread_lock_destroy (&(lock))
# define lock_lock(lock) glthread_lock_lock (&(lock))
# define lock_unlock(lock) glthread_lock_unlock (&(lock))
-#elif defined GNULIB_PTHREAD
+#elif defined GNULIB_PTHREAD && !defined USE_UNLOCKED_IO
# include
# define lock_define(name) pthread_mutex_t name;
# define lock_init(lock) pthread_mutex_init (&(lock), 0)
@@ -87,7 +87,6 @@
# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
# define _RE_DEFINE_LOCALE_FUNCTIONS 1
# include
-# include
# include
# endif
#endif
@@ -137,7 +136,10 @@
# undef __wctype
# undef __iswctype
# define __wctype wctype
+# define __iswalnum iswalnum
# define __iswctype iswctype
+# define __towlower towlower
+# define __towupper towupper
# define __btowc btowc
# define __mbrtowc mbrtowc
# define __wcrtomb wcrtomb
@@ -447,23 +449,23 @@ typedef struct re_dfa_t re_dfa_t;
#ifndef _LIBC
# define internal_function
+# define IS_IN(libc) false
#endif
-#ifndef NOT_IN_libc
static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
Idx new_buf_len)
internal_function;
-# ifdef RE_ENABLE_I18N
+#ifdef RE_ENABLE_I18N
static void build_wcs_buffer (re_string_t *pstr) internal_function;
static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
internal_function;
-# endif /* RE_ENABLE_I18N */
+#endif /* RE_ENABLE_I18N */
static void build_upper_buffer (re_string_t *pstr) internal_function;
static void re_string_translate_buffer (re_string_t *pstr) internal_function;
static unsigned int re_string_context_at (const re_string_t *input, Idx idx,
int eflags)
internal_function __attribute__ ((pure));
-#endif
+
#define re_string_peek_byte(pstr, offset) \
((pstr)->mbs[(pstr)->cur_idx + offset])
#define re_string_fetch_byte(pstr) \
@@ -556,7 +558,7 @@ typedef struct bin_tree_storage_t bin_tree_storage_t;
#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
-#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_WORD_CHAR(ch) (__iswalnum (ch) || (ch) == L'_')
#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
@@ -860,15 +862,17 @@ re_string_wchar_at (const re_string_t *pstr, Idx idx)
return (wint_t) pstr->wcs[idx];
}
-# ifndef NOT_IN_libc
+# ifdef _LIBC
+# include
+# endif
+
static int
internal_function __attribute__ ((pure, unused))
re_string_elem_size_at (const re_string_t *pstr, Idx idx)
{
-# ifdef _LIBC
+# ifdef _LIBC
const unsigned char *p, *extra;
const int32_t *table, *indirect;
-# include
uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
if (nrules != 0)
@@ -879,14 +883,13 @@ re_string_elem_size_at (const re_string_t *pstr, Idx idx)
indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
_NL_COLLATE_INDIRECTMB);
p = pstr->mbs + idx;
- findidx (&p, pstr->len - idx);
+ findidx (table, indirect, extra, &p, pstr->len - idx);
return p - pstr->mbs - idx;
}
else
-# endif /* _LIBC */
+# endif /* _LIBC */
return 1;
}
-# endif
#endif /* RE_ENABLE_I18N */
#ifndef __GNUC_PREREQ
diff --git a/lib/regexec.c b/lib/regexec.c
index 05a8e807e..db50a564a 100644
--- a/lib/regexec.c
+++ b/lib/regexec.c
@@ -1,5 +1,5 @@
/* Extended regular expression matching and search library.
- Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Isamu Hasegawa .
@@ -3776,6 +3776,10 @@ group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
one collating element like '.', '[a-z]', opposite to the other nodes
can only accept one byte. */
+# ifdef _LIBC
+# include
+# endif
+
static int
internal_function
check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
@@ -3895,8 +3899,6 @@ check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
const int32_t *table, *indirect;
const unsigned char *weights, *extra;
const char *collseqwc;
- /* This #include defines a local function! */
-# include
/* match with collating_symbol? */
if (cset->ncoll_syms)
@@ -3953,7 +3955,7 @@ check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
indirect = (const int32_t *)
_NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
- int32_t idx = findidx (&cp, elem_len);
+ int32_t idx = findidx (table, indirect, extra, &cp, elem_len);
if (idx > 0)
for (i = 0; i < cset->nequiv_classes; ++i)
{
diff --git a/lib/rename.c b/lib/rename.c
index 1cd4e6da3..feb2bd11f 100644
--- a/lib/rename.c
+++ b/lib/rename.c
@@ -1,6 +1,6 @@
/* Work around rename bugs in some systems.
- Copyright (C) 2001-2003, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003, 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -285,7 +285,7 @@ rpl_rename (char const *src, char const *dst)
char *dst_temp = (char *) dst;
bool src_slash;
bool dst_slash;
- bool dst_exists;
+ bool dst_exists _GL_UNUSED;
int ret_val = -1;
int rename_errno = ENOTDIR;
struct stat src_st;
@@ -462,7 +462,9 @@ rpl_rename (char const *src, char const *dst)
ret_val = rename (src_temp, dst_temp);
rename_errno = errno;
- out:
+
+ out: _GL_UNUSED_LABEL;
+
if (src_temp != src)
free (src_temp);
if (dst_temp != dst)
diff --git a/lib/rmdir.c b/lib/rmdir.c
index 964dd2028..98dc37f51 100644
--- a/lib/rmdir.c
+++ b/lib/rmdir.c
@@ -1,6 +1,6 @@
/* Work around rmdir bugs.
- Copyright (C) 1988, 1990, 1999, 2003-2006, 2009-2014 Free Software
+ Copyright (C) 1988, 1990, 1999, 2003-2006, 2009-2015 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/round.c b/lib/round.c
index d1c2aac5a..ea43e2f7b 100644
--- a/lib/round.c
+++ b/lib/round.c
@@ -1,5 +1,5 @@
/* Round toward nearest, breaking ties away from zero.
- Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/safe-read.c b/lib/safe-read.c
index 6c9639f40..2b29d7b5a 100644
--- a/lib/safe-read.c
+++ b/lib/safe-read.c
@@ -1,6 +1,6 @@
/* An interface to read and write that retries after interrupts.
- Copyright (C) 1993-1994, 1998, 2002-2006, 2009-2014 Free Software
+ Copyright (C) 1993-1994, 1998, 2002-2006, 2009-2015 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/safe-read.h b/lib/safe-read.h
index 6cd5f68fc..bdefa97f1 100644
--- a/lib/safe-read.h
+++ b/lib/safe-read.h
@@ -1,5 +1,5 @@
/* An interface to read() that retries after interrupts.
- Copyright (C) 2002, 2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/safe-write.c b/lib/safe-write.c
index 3e7ffd627..0828ccb36 100644
--- a/lib/safe-write.c
+++ b/lib/safe-write.c
@@ -1,5 +1,5 @@
/* An interface to write that retries after interrupts.
- Copyright (C) 2002, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/safe-write.h b/lib/safe-write.h
index 45a61463a..c156ae783 100644
--- a/lib/safe-write.h
+++ b/lib/safe-write.h
@@ -1,5 +1,5 @@
/* An interface to write() that retries after interrupts.
- Copyright (C) 2002, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/same-inode.h b/lib/same-inode.h
index f85a3cce8..b91a02b95 100644
--- a/lib/same-inode.h
+++ b/lib/same-inode.h
@@ -1,6 +1,6 @@
/* Determine whether two stat buffers refer to the same file.
- Copyright (C) 2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/secure_getenv.c b/lib/secure_getenv.c
index 7b86173bb..3a5216279 100644
--- a/lib/secure_getenv.c
+++ b/lib/secure_getenv.c
@@ -1,6 +1,6 @@
/* Look up an environment variable more securely.
- Copyright 2013-2014 Free Software Foundation, Inc.
+ Copyright 2013-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/select.c b/lib/select.c
index a31f90224..6f519878c 100644
--- a/lib/select.c
+++ b/lib/select.c
@@ -1,7 +1,7 @@
/* Emulation for select(2)
Contributed by Paolo Bonzini.
- Copyright 2008-2014 Free Software Foundation, Inc.
+ Copyright 2008-2015 Free Software Foundation, Inc.
This file is part of gnulib.
@@ -82,9 +82,11 @@ typedef DWORD (WINAPI *PNtQueryInformationFile)
#define PIPE_BUF 512
#endif
-/* Optimized test whether a HANDLE refers to a console.
- See . */
-#define IsConsoleHandle(h) (((intptr_t) (h) & 3) == 3)
+static BOOL IsConsoleHandle (HANDLE h)
+{
+ DWORD mode;
+ return GetConsoleMode (h, &mode) != 0;
+}
static BOOL
IsSocketHandle (HANDLE h)
@@ -252,6 +254,7 @@ rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
MSG msg;
int i, fd, rc;
+ clock_t tend;
if (nfds > FD_SETSIZE)
nfds = FD_SETSIZE;
@@ -388,6 +391,10 @@ rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
/* Place a sentinel at the end of the array. */
handle_array[nhandles] = NULL;
+ /* When will the waiting period expire? */
+ if (wait_timeout != INFINITE)
+ tend = clock () + wait_timeout;
+
restart:
if (wait_timeout == 0 || nsock == 0)
rc = 0;
@@ -408,6 +415,16 @@ restart:
wait_timeout = 0;
}
+ /* How much is left to wait? */
+ if (wait_timeout != INFINITE)
+ {
+ clock_t tnow = clock ();
+ if (tend >= tnow)
+ wait_timeout = tend - tnow;
+ else
+ wait_timeout = 0;
+ }
+
for (;;)
{
ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
@@ -453,7 +470,16 @@ restart:
}
}
- if (rc == 0 && wait_timeout == INFINITE)
+ if (rc == 0
+ && (wait_timeout == INFINITE
+ /* If NHANDLES > 1, but no bits are set, it means we've
+ been told incorrectly that some handle was signaled.
+ This happens with anonymous pipes, which always cause
+ MsgWaitForMultipleObjects to exit immediately, but no
+ data is found ready to be read by windows_poll_handle.
+ To avoid a total failure (whereby we return zero and
+ don't wait at all), let's poll in a more busy loop. */
+ || (wait_timeout != 0 && nhandles > 1)))
{
/* Sleep 1 millisecond to avoid busy wait and retry with the
original fd_sets. */
@@ -463,6 +489,8 @@ restart:
SleepEx (1, TRUE);
goto restart;
}
+ if (timeout && wait_timeout == 0 && rc == 0)
+ timeout->tv_sec = timeout->tv_usec = 0;
}
/* Now fill in the results. */
diff --git a/lib/send.c b/lib/send.c
index 9e70c91af..54315d359 100644
--- a/lib/send.c
+++ b/lib/send.c
@@ -1,6 +1,6 @@
/* send.c --- wrappers for Windows send function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sendto.c b/lib/sendto.c
index 69b8ebc9f..f7f11d602 100644
--- a/lib/sendto.c
+++ b/lib/sendto.c
@@ -1,6 +1,6 @@
/* sendto.c --- wrappers for Windows sendto function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/setenv.c b/lib/setenv.c
index 50e686025..689b404d0 100644
--- a/lib/setenv.c
+++ b/lib/setenv.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1992, 1995-2003, 2005-2014 Free Software Foundation, Inc.
+/* Copyright (C) 1992, 1995-2003, 2005-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/setsockopt.c b/lib/setsockopt.c
index 2b905daa0..56b4827c2 100644
--- a/lib/setsockopt.c
+++ b/lib/setsockopt.c
@@ -1,6 +1,6 @@
/* setsockopt.c --- wrappers for Windows setsockopt function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/shutdown.c b/lib/shutdown.c
index 54b7728dd..29c325ef0 100644
--- a/lib/shutdown.c
+++ b/lib/shutdown.c
@@ -1,6 +1,6 @@
/* shutdown.c --- wrappers for Windows shutdown function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/signal.in.h b/lib/signal.in.h
index 057fa9ef5..3ea23eee5 100644
--- a/lib/signal.in.h
+++ b/lib/signal.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -55,11 +55,13 @@
#ifndef _@GUARD_PREFIX@_SIGNAL_H
#define _@GUARD_PREFIX@_SIGNAL_H
-/* Mac OS X 10.3, FreeBSD 6.4, OpenBSD 3.8, OSF/1 4.0, Solaris 2.6 declare
- pthread_sigmask in , not in .
+/* Mac OS X 10.3, FreeBSD 6.4, OpenBSD 3.8, OSF/1 4.0, Solaris 2.6, Android
+ declare pthread_sigmask in , not in .
But avoid namespace pollution on glibc systems.*/
#if (@GNULIB_PTHREAD_SIGMASK@ || defined GNULIB_POSIXCHECK) \
- && ((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined __osf__ || defined __sun) \
+ && ((defined __APPLE__ && defined __MACH__) \
+ || defined __FreeBSD__ || defined __OpenBSD__ || defined __osf__ \
+ || defined __sun || defined __ANDROID__) \
&& ! defined __GLIBC__
# include
#endif
diff --git a/lib/signbitd.c b/lib/signbitd.c
index 1efb6e6a2..03beeecfb 100644
--- a/lib/signbitd.c
+++ b/lib/signbitd.c
@@ -1,5 +1,5 @@
/* signbit() macro: Determine the sign bit of a floating-point number.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/signbitf.c b/lib/signbitf.c
index 3240e4ec0..51a685234 100644
--- a/lib/signbitf.c
+++ b/lib/signbitf.c
@@ -1,5 +1,5 @@
/* signbit() macro: Determine the sign bit of a floating-point number.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/signbitl.c b/lib/signbitl.c
index 3f847257c..0142c33df 100644
--- a/lib/signbitl.c
+++ b/lib/signbitl.c
@@ -1,5 +1,5 @@
/* signbit() macro: Determine the sign bit of a floating-point number.
- Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/size_max.h b/lib/size_max.h
index 680ca0fff..935cf8978 100644
--- a/lib/size_max.h
+++ b/lib/size_max.h
@@ -1,5 +1,5 @@
/* size_max.h -- declare SIZE_MAX through system headers
- Copyright (C) 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2009-2015 Free Software Foundation, Inc.
Written by Simon Josefsson.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/snprintf.c b/lib/snprintf.c
index 0b8cbf88f..f5a38629b 100644
--- a/lib/snprintf.c
+++ b/lib/snprintf.c
@@ -1,5 +1,5 @@
/* Formatted output to strings.
- Copyright (C) 2004, 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2006-2015 Free Software Foundation, Inc.
Written by Simon Josefsson and Paul Eggert.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/socket.c b/lib/socket.c
index c10d4f6ad..da46cb907 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -1,6 +1,6 @@
/* socket.c --- wrappers for Windows socket function
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sockets.c b/lib/sockets.c
index 98fe879ee..acf41b526 100644
--- a/lib/sockets.c
+++ b/lib/sockets.c
@@ -1,6 +1,6 @@
/* sockets.c --- wrappers for Windows socket functions
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -121,8 +121,11 @@ gl_sockets_startup (int version _GL_UNUSED)
if (err != 0)
return 1;
- if (data.wVersion < version)
- return 2;
+ if (data.wVersion != version)
+ {
+ WSACleanup ();
+ return 2;
+ }
if (initialized_sockets_version == 0)
register_fd_hook (close_fd_maybe_socket, ioctl_fd_maybe_socket,
diff --git a/lib/sockets.h b/lib/sockets.h
index dd99ec172..66b947224 100644
--- a/lib/sockets.h
+++ b/lib/sockets.h
@@ -1,6 +1,6 @@
/* sockets.h - wrappers for Windows socket functions
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -20,11 +20,11 @@
#ifndef SOCKETS_H
# define SOCKETS_H 1
-#define SOCKETS_1_0 0x100 /* don't use - does not work on Windows XP */
-#define SOCKETS_1_1 0x101
-#define SOCKETS_2_0 0x200 /* don't use - does not work on Windows XP */
-#define SOCKETS_2_1 0x201
-#define SOCKETS_2_2 0x202
+#define SOCKETS_1_0 0x0001
+#define SOCKETS_1_1 0x0101
+#define SOCKETS_2_0 0x0002
+#define SOCKETS_2_1 0x0102
+#define SOCKETS_2_2 0x0202
int gl_sockets_startup (int version)
#if !WINDOWS_SOCKETS
diff --git a/lib/stat-time.h b/lib/stat-time.h
index 570001361..82b83acef 100644
--- a/lib/stat-time.h
+++ b/lib/stat-time.h
@@ -1,6 +1,6 @@
/* stat-related time functions.
- Copyright (C) 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -54,7 +54,7 @@ _GL_INLINE_HEADER_BEGIN
#endif
/* Return the nanosecond component of *ST's access time. */
-_GL_STAT_TIME_INLINE long int
+_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
get_stat_atime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
@@ -67,7 +67,7 @@ get_stat_atime_ns (struct stat const *st)
}
/* Return the nanosecond component of *ST's status change time. */
-_GL_STAT_TIME_INLINE long int
+_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
get_stat_ctime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
@@ -80,7 +80,7 @@ get_stat_ctime_ns (struct stat const *st)
}
/* Return the nanosecond component of *ST's data modification time. */
-_GL_STAT_TIME_INLINE long int
+_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
get_stat_mtime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
@@ -93,7 +93,7 @@ get_stat_mtime_ns (struct stat const *st)
}
/* Return the nanosecond component of *ST's birth time. */
-_GL_STAT_TIME_INLINE long int
+_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
get_stat_birthtime_ns (struct stat const *st)
{
# if defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
@@ -108,7 +108,7 @@ get_stat_birthtime_ns (struct stat const *st)
}
/* Return *ST's access time. */
-_GL_STAT_TIME_INLINE struct timespec
+_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
get_stat_atime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
@@ -122,7 +122,7 @@ get_stat_atime (struct stat const *st)
}
/* Return *ST's status change time. */
-_GL_STAT_TIME_INLINE struct timespec
+_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
get_stat_ctime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
@@ -136,7 +136,7 @@ get_stat_ctime (struct stat const *st)
}
/* Return *ST's data modification time. */
-_GL_STAT_TIME_INLINE struct timespec
+_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
get_stat_mtime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
@@ -151,7 +151,7 @@ get_stat_mtime (struct stat const *st)
/* Return *ST's birth time, if available; otherwise return a value
with tv_sec and tv_nsec both equal to -1. */
-_GL_STAT_TIME_INLINE struct timespec
+_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
get_stat_birthtime (struct stat const *st)
{
struct timespec t;
diff --git a/lib/stat.c b/lib/stat.c
index 60bbd693e..47f6c3b97 100644
--- a/lib/stat.c
+++ b/lib/stat.c
@@ -1,5 +1,5 @@
/* Work around platform bugs in stat.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/stdalign.in.h b/lib/stdalign.in.h
index 29861efe1..31bb7bce0 100644
--- a/lib/stdalign.in.h
+++ b/lib/stdalign.in.h
@@ -1,6 +1,6 @@
/* A substitute for ISO C11 .
- Copyright 2011-2014 Free Software Foundation, Inc.
+ Copyright 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -52,7 +52,10 @@
#undef _Alignas
#undef _Alignof
-#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
+/* GCC releases before GCC 4.9 had a bug in _Alignof. See GCC bug 52023
+ . */
+#if (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 \
+ || (defined __GNUC__ && __GNUC__ < 4 + (__GNUC_MINOR__ < 9)))
# ifdef __cplusplus
# if 201103 <= __cplusplus
# define _Alignof(type) alignof (type)
@@ -64,7 +67,9 @@
# define _Alignof(type) offsetof (struct { char __a; type __b; }, __b)
# endif
#endif
-#define alignof _Alignof
+#if ! (defined __cplusplus && 201103 <= __cplusplus)
+# define alignof _Alignof
+#endif
#define __alignof_is_defined 1
/* alignas (A), also known as _Alignas (A), aligns a variable or type
@@ -95,15 +100,21 @@
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
# if defined __cplusplus && 201103 <= __cplusplus
# define _Alignas(a) alignas (a)
-# elif (__GNUC__ || __HP_cc || __HP_aCC || __IBMC__ || __IBMCPP__ \
+# elif ((defined __APPLE__ && defined __MACH__ \
+ ? 4 < __GNUC__ + (1 <= __GNUC_MINOR__) \
+ : __GNUC__) \
+ || __HP_cc || __HP_aCC || __IBMC__ || __IBMCPP__ \
|| __ICC || 0x5110 <= __SUNPRO_C)
# define _Alignas(a) __attribute__ ((__aligned__ (a)))
# elif 1300 <= _MSC_VER
# define _Alignas(a) __declspec (align (a))
# endif
#endif
-#if defined _Alignas || (defined __STDC_VERSION && 201112 <= __STDC_VERSION__)
+#if ((defined _Alignas && ! (defined __cplusplus && 201103 <= __cplusplus)) \
+ || (defined __STDC_VERSION && 201112 <= __STDC_VERSION__))
# define alignas _Alignas
+#endif
+#if defined alignas || (defined __cplusplus && 201103 <= __cplusplus)
# define __alignas_is_defined 1
#endif
diff --git a/lib/stdbool.in.h b/lib/stdbool.in.h
index 2f34a13fb..fb1cde05f 100644
--- a/lib/stdbool.in.h
+++ b/lib/stdbool.in.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2003, 2006-2014 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2003, 2006-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/stddef.in.h b/lib/stddef.in.h
index 204c4bcf0..383d44135 100644
--- a/lib/stddef.in.h
+++ b/lib/stddef.in.h
@@ -1,6 +1,6 @@
/* A substitute for POSIX 2008 , for platforms that have issues.
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -39,7 +39,6 @@
# if !(defined _@GUARD_PREFIX@_STDDEF_H && defined _GL_STDDEF_WINT_T)
# ifdef __need_wint_t
-# undef _@GUARD_PREFIX@_STDDEF_H
# define _GL_STDDEF_WINT_T
# endif
# @INCLUDE_NEXT@ @NEXT_STDDEF_H@
@@ -54,33 +53,56 @@
# @INCLUDE_NEXT@ @NEXT_STDDEF_H@
-# ifndef _@GUARD_PREFIX@_STDDEF_H
-# define _@GUARD_PREFIX@_STDDEF_H
-
/* On NetBSD 5.0, the definition of NULL lacks proper parentheses. */
-#if @REPLACE_NULL@
-# undef NULL
-# ifdef __cplusplus
+# if (@REPLACE_NULL@ \
+ && (!defined _@GUARD_PREFIX@_STDDEF_H || defined _GL_STDDEF_WINT_T))
+# undef NULL
+# ifdef __cplusplus
/* ISO C++ says that the macro NULL must expand to an integer constant
expression, hence '((void *) 0)' is not allowed in C++. */
-# if __GNUG__ >= 3
+# if __GNUG__ >= 3
/* GNU C++ has a __null macro that behaves like an integer ('int' or
'long') but has the same size as a pointer. Use that, to avoid
warnings. */
-# define NULL __null
-# else
-# define NULL 0L
+# define NULL __null
+# else
+# define NULL 0L
+# endif
+# else
+# define NULL ((void *) 0)
+# endif
# endif
-# else
-# define NULL ((void *) 0)
-# endif
-#endif
+
+# ifndef _@GUARD_PREFIX@_STDDEF_H
+# define _@GUARD_PREFIX@_STDDEF_H
/* Some platforms lack wchar_t. */
#if !@HAVE_WCHAR_T@
# define wchar_t int
#endif
+/* Some platforms lack max_align_t. */
+#if !@HAVE_MAX_ALIGN_T@
+/* On the x86, the maximum storage alignment of double, long, etc. is 4,
+ but GCC's C11 ABI for x86 says that max_align_t has an alignment of 8,
+ and the C11 standard allows this. Work around this problem by
+ using __alignof__ (which returns 8 for double) rather than _Alignof
+ (which returns 4), and align each union member accordingly. */
+# ifdef __GNUC__
+# define _GL_STDDEF_ALIGNAS(type) \
+ __attribute__ ((__aligned__ (__alignof__ (type))))
+# else
+# define _GL_STDDEF_ALIGNAS(type) /* */
+# endif
+typedef union
+{
+ char *__p _GL_STDDEF_ALIGNAS (char *);
+ double __d _GL_STDDEF_ALIGNAS (double);
+ long double __ld _GL_STDDEF_ALIGNAS (long double);
+ long int __i _GL_STDDEF_ALIGNAS (long int);
+} max_align_t;
+#endif
+
# endif /* _@GUARD_PREFIX@_STDDEF_H */
# endif /* _@GUARD_PREFIX@_STDDEF_H */
#endif /* __need_XXX */
diff --git a/lib/stdint.in.h b/lib/stdint.in.h
index b1296f9ea..cf3655871 100644
--- a/lib/stdint.in.h
+++ b/lib/stdint.in.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001-2002, 2004-2014 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2002, 2004-2015 Free Software Foundation, Inc.
Written by Paul Eggert, Bruno Haible, Sam Steingold, Peter Burwood.
This file is part of gnulib.
@@ -38,8 +38,7 @@
other system header files; just include the system's .
Ideally we should test __BIONIC__ here, but it is only defined after
has been included; hence test __ANDROID__ instead. */
-#if defined __ANDROID__ \
- && defined _SYS_TYPES_H_ && !defined __need_size_t
+#if defined __ANDROID__ && defined _GL_INCLUDING_SYS_TYPES_H
# @INCLUDE_NEXT@ @NEXT_STDINT_H@
#else
diff --git a/lib/stdio.in.h b/lib/stdio.in.h
index faa778b1d..d9fd18561 100644
--- a/lib/stdio.in.h
+++ b/lib/stdio.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2004, 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -84,8 +84,13 @@
except that it indicates to GCC that the supported format string directives
are the ones of the system printf(), rather than the ones standardized by
ISO C99 and POSIX. */
-#define _GL_ATTRIBUTE_FORMAT_PRINTF_SYSTEM(formatstring_parameter, first_argument) \
+#if GNULIB_PRINTF_ATTRIBUTE_FLAVOR_GNU
+# define _GL_ATTRIBUTE_FORMAT_PRINTF_SYSTEM(formatstring_parameter, first_argument) \
+ _GL_ATTRIBUTE_FORMAT_PRINTF (formatstring_parameter, first_argument)
+#else
+# define _GL_ATTRIBUTE_FORMAT_PRINTF_SYSTEM(formatstring_parameter, first_argument) \
_GL_ATTRIBUTE_FORMAT ((__printf__, formatstring_parameter, first_argument))
+#endif
/* _GL_ATTRIBUTE_FORMAT_SCANF
indicates to GCC that the function takes a format string and arguments,
@@ -718,11 +723,10 @@ _GL_WARN_ON_USE (getline, "getline is unportable - "
so any use of gets warrants an unconditional warning; besides, C11
removed it. */
#undef gets
-#if HAVE_RAW_DECL_GETS
+#if HAVE_RAW_DECL_GETS && !defined __cplusplus
_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
#endif
-
#if @GNULIB_OBSTACK_PRINTF@ || @GNULIB_OBSTACK_PRINTF_POSIX@
struct obstack;
/* Grow an obstack with formatted output. Return the number of
diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h
index 57d32cc48..fc46a2115 100644
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 1995, 2001-2004, 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 1995, 2001-2004, 2006-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -520,6 +520,29 @@ _GL_CXXALIAS_SYS (putenv, int, (char *string));
_GL_CXXALIASWARN (putenv);
#endif
+#if @GNULIB_QSORT_R@
+# if @REPLACE_QSORT_R@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef qsort_r
+# define qsort_r rpl_qsort_r
+# endif
+_GL_FUNCDECL_RPL (qsort_r, void, (void *base, size_t nmemb, size_t size,
+ int (*compare) (void const *, void const *,
+ void *),
+ void *arg) _GL_ARG_NONNULL ((1, 4)));
+_GL_CXXALIAS_RPL (qsort_r, void, (void *base, size_t nmemb, size_t size,
+ int (*compare) (void const *, void const *,
+ void *),
+ void *arg));
+# else
+_GL_CXXALIAS_SYS (qsort_r, void, (void *base, size_t nmemb, size_t size,
+ int (*compare) (void const *, void const *,
+ void *),
+ void *arg));
+# endif
+_GL_CXXALIASWARN (qsort_r);
+#endif
+
#if @GNULIB_RANDOM_R@
# if !@HAVE_RANDOM_R@
diff --git a/lib/strdup.c b/lib/strdup.c
index bde582927..7bbc59a74 100644
--- a/lib/strdup.c
+++ b/lib/strdup.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991, 1996-1998, 2002-2004, 2006-2007, 2009-2014 Free Software
+/* Copyright (C) 1991, 1996-1998, 2002-2004, 2006-2007, 2009-2015 Free Software
Foundation, Inc.
This file is part of the GNU C Library.
diff --git a/lib/streq.h b/lib/streq.h
index 0f7bc72b2..85ce271ab 100644
--- a/lib/streq.h
+++ b/lib/streq.h
@@ -1,5 +1,5 @@
/* Optimized string comparison.
- Copyright (C) 2001-2002, 2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/strftime.c b/lib/strftime.c
index eb458d117..876d16e2b 100644
--- a/lib/strftime.c
+++ b/lib/strftime.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2001, 2003-2007, 2009-2014 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2001, 2003-2007, 2009-2015 Free Software Foundation, Inc.
NOTE: The canonical source of this file is maintained with the GNU C Library.
Bugs can be reported to bug-glibc@prep.ai.mit.edu.
@@ -30,6 +30,7 @@
# else
# include "strftime.h"
# endif
+# include "time-internal.h"
#endif
#include
@@ -121,22 +122,11 @@ extern char *tzname[];
#ifdef _LIBC
+# define mktime_z(tz, tm) mktime (tm)
# define tzname __tzname
# define tzset __tzset
#endif
-#if !HAVE_TM_GMTOFF
-/* Portable standalone applications should supply a "time.h" that
- declares a POSIX-compliant localtime_r, for the benefit of older
- implementations that lack localtime_r or have a nonstandard one.
- See the gnulib time_r module for one way to implement this. */
-# undef __gmtime_r
-# undef __localtime_r
-# define __gmtime_r gmtime_r
-# define __localtime_r localtime_r
-#endif
-
-
#ifndef FPRINTFTIME
# define FPRINTFTIME 0
#endif
@@ -385,12 +375,7 @@ iso_week_days (int yday, int wday)
/* When compiling this file, GNU applications can #define my_strftime
to a symbol (typically nstrftime) to get an extended strftime with
- extra arguments UT and NS. Emacs is a special case for now, but
- this Emacs-specific code can be removed once Emacs's config.h
- defines my_strftime. */
-#if defined emacs && !defined my_strftime
-# define my_strftime nstrftime
-#endif
+ extra arguments TZ and NS. */
#if FPRINTFTIME
# undef my_strftime
@@ -398,8 +383,9 @@ iso_week_days (int yday, int wday)
#endif
#ifdef my_strftime
-# define extra_args , ut, ns
-# define extra_args_spec , int ut, int ns
+# undef HAVE_TZSET
+# define extra_args , tz, ns
+# define extra_args_spec , timezone_t tz, int ns
#else
# if defined COMPILE_WIDE
# define my_strftime wcsftime
@@ -411,7 +397,7 @@ iso_week_days (int yday, int wday)
# define extra_args
# define extra_args_spec
/* We don't have this information in general. */
-# define ut 0
+# define tz 1
# define ns 0
#endif
@@ -454,6 +440,9 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
# define aw_len STRLEN (a_wkday)
# define am_len STRLEN (a_month)
# define ap_len STRLEN (ampm)
+#endif
+#if HAVE_TZNAME
+ char **tzname_vec = tzname;
#endif
const char *zone;
size_t i = 0;
@@ -483,20 +472,29 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
zone = (const char *) tp->tm_zone;
#endif
#if HAVE_TZNAME
- if (ut)
+ if (!tz)
{
if (! (zone && *zone))
zone = "GMT";
}
else
{
+# if !HAVE_TM_ZONE
+ /* Infer the zone name from *TZ instead of from TZNAME. */
+ tzname_vec = tz->tzname_copy;
+# endif
/* POSIX.1 requires that local time zone information be used as
though strftime called tzset. */
# if HAVE_TZSET
tzset ();
# endif
}
+ /* The tzset() call might have changed the value. */
+ if (!(zone && *zone) && tp->tm_isdst >= 0)
+ zone = tzname_vec[tp->tm_isdst != 0];
#endif
+ if (! zone)
+ zone = "";
if (hour12 > 12)
hour12 -= 12;
@@ -681,24 +679,44 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
switch (format_char)
{
#define DO_NUMBER(d, v) \
- digits = d; \
- number_value = v; goto do_number
+ do \
+ { \
+ digits = d; \
+ number_value = v; \
+ goto do_number; \
+ } \
+ while (0)
#define DO_SIGNED_NUMBER(d, negative, v) \
- digits = d; \
- negative_number = negative; \
- u_number_value = v; goto do_signed_number
+ do \
+ { \
+ digits = d; \
+ negative_number = negative; \
+ u_number_value = v; \
+ goto do_signed_number; \
+ } \
+ while (0)
/* The mask is not what you might think.
When the ordinal i'th bit is set, insert a colon
before the i'th digit of the time zone representation. */
#define DO_TZ_OFFSET(d, negative, mask, v) \
- digits = d; \
- negative_number = negative; \
- tz_colon_mask = mask; \
- u_number_value = v; goto do_tz_offset
+ do \
+ { \
+ digits = d; \
+ negative_number = negative; \
+ tz_colon_mask = mask; \
+ u_number_value = v; \
+ goto do_tz_offset; \
+ } \
+ while (0)
#define DO_NUMBER_SPACEPAD(d, v) \
- digits = d; \
- number_value = v; goto do_number_spacepad
+ do \
+ { \
+ digits = d; \
+ number_value = v; \
+ goto do_number_spacepad; \
+ } \
+ while (0)
case L_('%'):
if (modifier != 0)
@@ -1124,7 +1142,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
time_t t;
ltm = *tp;
- t = mktime (<m);
+ t = mktime_z (tz, <m);
/* Generate string value for T using time_t arithmetic;
this works even if sizeof (long) < sizeof (time_t). */
@@ -1265,9 +1283,9 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
}
if (modifier == L_('O'))
goto bad_format;
- else
- DO_SIGNED_NUMBER (4, tp->tm_year < -TM_YEAR_BASE,
- tp->tm_year + (unsigned int) TM_YEAR_BASE);
+
+ DO_SIGNED_NUMBER (4, tp->tm_year < -TM_YEAR_BASE,
+ tp->tm_year + (unsigned int) TM_YEAR_BASE);
case L_('y'):
if (modifier == L_('E'))
@@ -1299,14 +1317,6 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
to_lowcase = true;
}
-#if HAVE_TZNAME
- /* The tzset() call might have changed the value. */
- if (!(zone && *zone) && tp->tm_isdst >= 0)
- zone = tzname[tp->tm_isdst != 0];
-#endif
- if (! zone)
- zone = "";
-
#ifdef COMPILE_WIDE
{
/* The zone string is always given in multibyte form. We have
@@ -1346,7 +1356,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
#if HAVE_TM_GMTOFF
diff = tp->tm_gmtoff;
#else
- if (ut)
+ if (!tz)
diff = 0;
else
{
@@ -1355,7 +1365,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
time_t lt;
ltm = *tp;
- lt = mktime (<m);
+ lt = mktime_z (tz, <m);
if (lt == (time_t) -1)
{
@@ -1364,7 +1374,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
occurred. */
struct tm tm;
- if (! __localtime_r (<, &tm)
+ if (! localtime_rz (tz, <, &tm)
|| ((ltm.tm_sec ^ tm.tm_sec)
| (ltm.tm_min ^ tm.tm_min)
| (ltm.tm_hour ^ tm.tm_hour)
@@ -1374,7 +1384,7 @@ strftime_case_ (bool upcase, STREAM_OR_CHAR_T *s,
break;
}
- if (! __gmtime_r (<, >m))
+ if (! localtime_rz (0, <, >m))
break;
diff = tm_diff (<m, >m);
@@ -1453,15 +1463,3 @@ my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize)
#if defined _LIBC && ! FPRINTFTIME
libc_hidden_def (my_strftime)
#endif
-
-
-#if defined emacs && ! FPRINTFTIME
-/* For Emacs we have a separate interface which corresponds to the normal
- strftime function plus the ut argument, but without the ns argument. */
-size_t
-emacs_strftimeu (char *s, size_t maxsize, const char *format,
- const struct tm *tp, int ut)
-{
- return my_strftime (s, maxsize, format, tp, ut, 0);
-}
-#endif
diff --git a/lib/strftime.h b/lib/strftime.h
index a394640e6..8756acd02 100644
--- a/lib/strftime.h
+++ b/lib/strftime.h
@@ -1,6 +1,6 @@
/* declarations for strftime.c
- Copyright (C) 2002, 2004, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2004, 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -23,11 +23,10 @@ extern "C" {
/* Just like strftime, but with two more arguments:
POSIX requires that strftime use the local timezone information.
- When __UTC is nonzero and tm->tm_zone is NULL or the empty string,
- use UTC instead. Use __NS as the number of nanoseconds in the
- %N directive. */
+ Use the timezone __TZ instead. Use __NS as the number of
+ nanoseconds in the %N directive. */
size_t nstrftime (char *, size_t, char const *, struct tm const *,
- int __utc, int __ns);
+ timezone_t __tz, int __ns);
#ifdef __cplusplus
}
diff --git a/lib/striconveh.c b/lib/striconveh.c
index 1a2f62e44..a9c9b0020 100644
--- a/lib/striconveh.c
+++ b/lib/striconveh.c
@@ -1,5 +1,5 @@
/* Character set conversion with error handling.
- Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2015 Free Software Foundation, Inc.
Written by Bruno Haible and Simon Josefsson.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/striconveh.h b/lib/striconveh.h
index a4e425aa2..bea457b86 100644
--- a/lib/striconveh.h
+++ b/lib/striconveh.h
@@ -1,5 +1,5 @@
/* Character set conversion with error handling.
- Copyright (C) 2001-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible and Simon Josefsson.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/string.in.h b/lib/string.in.h
index eaaaa9dda..9a630b163 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 1995-1996, 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 1995-1996, 2001-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -15,16 +15,32 @@
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see . */
-#ifndef _@GUARD_PREFIX@_STRING_H
-
#if __GNUC__ >= 3
@PRAGMA_SYSTEM_HEADER@
#endif
@PRAGMA_COLUMNS@
+#if defined _GL_ALREADY_INCLUDING_STRING_H
+/* Special invocation convention:
+ - On OS X/NetBSD we have a sequence of nested includes
+ -> -> "string.h"
+ In this situation system _chk variants due to -D_FORTIFY_SOURCE
+ might be used after any replacements defined here. */
+
+#@INCLUDE_NEXT@ @NEXT_STRING_H@
+
+#else
+/* Normal invocation convention. */
+
+#ifndef _@GUARD_PREFIX@_STRING_H
+
+#define _GL_ALREADY_INCLUDING_STRING_H
+
/* The include_next requires a split double-inclusion guard. */
#@INCLUDE_NEXT@ @NEXT_STRING_H@
+#undef _GL_ALREADY_INCLUDING_STRING_H
+
#ifndef _@GUARD_PREFIX@_STRING_H
#define _@GUARD_PREFIX@_STRING_H
@@ -1027,3 +1043,4 @@ _GL_WARN_ON_USE (strverscmp, "strverscmp is unportable - "
#endif /* _@GUARD_PREFIX@_STRING_H */
#endif /* _@GUARD_PREFIX@_STRING_H */
+#endif
diff --git a/lib/stripslash.c b/lib/stripslash.c
index 22295e57a..86298d64f 100644
--- a/lib/stripslash.c
+++ b/lib/stripslash.c
@@ -1,6 +1,6 @@
/* stripslash.c -- remove redundant trailing slashes from a file name
- Copyright (C) 1990, 2001, 2003-2006, 2009-2014 Free Software Foundation,
+ Copyright (C) 1990, 2001, 2003-2006, 2009-2015 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/sys_file.in.h b/lib/sys_file.in.h
index 1df6946a5..af5ba2f67 100644
--- a/lib/sys_file.in.h
+++ b/lib/sys_file.in.h
@@ -1,6 +1,6 @@
/* Provide a more complete sys/file.h.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sys_select.in.h b/lib/sys_select.in.h
index 7e5c3a389..20cfc9be2 100644
--- a/lib/sys_select.in.h
+++ b/lib/sys_select.in.h
@@ -1,5 +1,5 @@
/* Substitute for .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -24,8 +24,8 @@
On Cygwin, includes .
Simply delegate to the system's header in this case. */
#if (@HAVE_SYS_SELECT_H@ \
+ && !defined _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TYPES_H \
&& ((defined __osf__ && defined _SYS_TYPES_H_ \
- && !defined _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H \
&& defined _OSF_SOURCE) \
|| (defined __sun && defined _SYS_TYPES_H \
&& (! (defined _XOPEN_SOURCE || defined _POSIX_C_SOURCE) \
@@ -36,12 +36,13 @@
#elif (@HAVE_SYS_SELECT_H@ \
&& (defined _CYGWIN_SYS_TIME_H \
- || (defined __osf__ && defined _SYS_TIME_H_ \
- && !defined _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H \
- && defined _OSF_SOURCE) \
- || (defined __sun && defined _SYS_TIME_H \
- && (! (defined _XOPEN_SOURCE || defined _POSIX_C_SOURCE) \
- || defined __EXTENSIONS__))))
+ || (!defined _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H \
+ && ((defined __osf__ && defined _SYS_TIME_H_ \
+ && defined _OSF_SOURCE) \
+ || (defined __sun && defined _SYS_TIME_H \
+ && (! (defined _XOPEN_SOURCE \
+ || defined _POSIX_C_SOURCE) \
+ || defined __EXTENSIONS__))))))
# define _GL_SYS_SELECT_H_REDIRECT_FROM_SYS_TIME_H
# @INCLUDE_NEXT@ @NEXT_SYS_SELECT_H@
diff --git a/lib/sys_socket.c b/lib/sys_socket.c
index 3f017f8fc..3b261da03 100644
--- a/lib/sys_socket.c
+++ b/lib/sys_socket.c
@@ -1,3 +1,4 @@
#include
#define _GL_SYS_SOCKET_INLINE _GL_EXTERN_INLINE
#include "sys/socket.h"
+typedef int dummy;
diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h
index 0cbc3e4fe..7f1163506 100644
--- a/lib/sys_socket.in.h
+++ b/lib/sys_socket.in.h
@@ -1,6 +1,6 @@
/* Provide a sys/socket header file for systems lacking it (read: MinGW)
and for systems where it is incomplete.
- Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2015 Free Software Foundation, Inc.
Written by Simon Josefsson.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/sys_stat.in.h b/lib/sys_stat.in.h
index 32c23a055..c8cb6a5e5 100644
--- a/lib/sys_stat.in.h
+++ b/lib/sys_stat.in.h
@@ -1,5 +1,5 @@
/* Provide a more complete sys/stat header file.
- Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sys_time.in.h b/lib/sys_time.in.h
index f19326e02..e91bbfe19 100644
--- a/lib/sys_time.in.h
+++ b/lib/sys_time.in.h
@@ -1,6 +1,6 @@
/* Provide a more complete sys/time.h.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sys_times.in.h b/lib/sys_times.in.h
index b3babfb80..9ca247c64 100644
--- a/lib/sys_times.in.h
+++ b/lib/sys_times.in.h
@@ -1,5 +1,5 @@
/* Provide a sys/times.h header file.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/sys_types.in.h b/lib/sys_types.in.h
index c8d0bb48f..f313b85b3 100644
--- a/lib/sys_types.in.h
+++ b/lib/sys_types.in.h
@@ -1,6 +1,6 @@
/* Provide a more complete sys/types.h.
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -23,7 +23,9 @@
#ifndef _@GUARD_PREFIX@_SYS_TYPES_H
/* The include_next requires a split double-inclusion guard. */
+# define _GL_INCLUDING_SYS_TYPES_H
#@INCLUDE_NEXT@ @NEXT_SYS_TYPES_H@
+# undef _GL_INCLUDING_SYS_TYPES_H
#ifndef _@GUARD_PREFIX@_SYS_TYPES_H
#define _@GUARD_PREFIX@_SYS_TYPES_H
diff --git a/lib/sys_uio.in.h b/lib/sys_uio.in.h
index 8cad7de32..0646ed2bc 100644
--- a/lib/sys_uio.in.h
+++ b/lib/sys_uio.in.h
@@ -1,5 +1,5 @@
/* Substitute for .
- Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ Copyright (C) 2011-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/tempname.c b/lib/tempname.c
index f0f7e7f29..da0c41c16 100644
--- a/lib/tempname.c
+++ b/lib/tempname.c
@@ -1,6 +1,6 @@
/* tempname.c - generate the name of a temporary file.
- Copyright (C) 1991-2003, 2005-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1991-2003, 2005-2007, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -62,6 +62,7 @@
# define struct_stat64 struct stat64
#else
# define struct_stat64 struct stat
+# define __try_tempname try_tempname
# define __gen_tempname gen_tempname
# define __getpid getpid
# define __gettimeofday gettimeofday
@@ -176,21 +177,9 @@ __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-/* Generate a temporary file name based on TMPL. TMPL must match the
- rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
- The name constructed does not exist at the time of the call to
- __gen_tempname. TMPL is overwritten with the result.
-
- KIND may be one of:
- __GT_NOCREATE: simply verify that the name does not exist
- at the time of the call.
- __GT_FILE: create the file using open(O_CREAT|O_EXCL)
- and return a read-write fd. The file is mode 0600.
- __GT_DIR: create a directory, which will be mode 0700.
-
- We use a clever algorithm to get hard-to-predict names. */
int
-__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+__try_tempname (char *tmpl, int suffixlen, void *args,
+ int (*tryfunc) (char *, void *))
{
int len;
char *XXXXXX;
@@ -199,7 +188,6 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
unsigned int count;
int fd = -1;
int save_errno = errno;
- struct_stat64 st;
/* A lower bound on the number of temporary files to attempt to
generate. The maximum total number of temporary file names that
@@ -256,41 +244,7 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
v /= 62;
XXXXXX[5] = letters[v % 62];
- switch (kind)
- {
- case __GT_FILE:
- fd = __open (tmpl,
- (flags & ~O_ACCMODE)
- | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
- break;
-
- case __GT_DIR:
- fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
- break;
-
- case __GT_NOCREATE:
- /* This case is backward from the other three. __gen_tempname
- succeeds if __xstat fails because the name does not exist.
- Note the continue to bypass the common logic at the bottom
- of the loop. */
- if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
- {
- if (errno == ENOENT)
- {
- __set_errno (save_errno);
- return 0;
- }
- else
- /* Give up now. */
- return -1;
- }
- continue;
-
- default:
- assert (! "invalid KIND in __gen_tempname");
- abort ();
- }
-
+ fd = tryfunc (tmpl, args);
if (fd >= 0)
{
__set_errno (save_errno);
@@ -304,3 +258,67 @@ __gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
__set_errno (EEXIST);
return -1;
}
+
+static int
+try_file (char *tmpl, void *flags)
+{
+ int *openflags = flags;
+ return __open (tmpl,
+ (*openflags & ~O_ACCMODE)
+ | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+}
+
+static int
+try_dir (char *tmpl, void *flags _GL_UNUSED)
+{
+ return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
+}
+
+static int
+try_nocreate (char *tmpl, void *flags _GL_UNUSED)
+{
+ struct_stat64 st;
+
+ if (__lxstat64 (_STAT_VER, tmpl, &st) == 0)
+ __set_errno (EEXIST);
+ return errno == ENOENT ? 0 : -1;
+}
+
+/* Generate a temporary file name based on TMPL. TMPL must match the
+ rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
+ The name constructed does not exist at the time of the call to
+ __gen_tempname. TMPL is overwritten with the result.
+
+ KIND may be one of:
+ __GT_NOCREATE: simply verify that the name does not exist
+ at the time of the call.
+ __GT_FILE: create the file using open(O_CREAT|O_EXCL)
+ and return a read-write fd. The file is mode 0600.
+ __GT_DIR: create a directory, which will be mode 0700.
+
+ We use a clever algorithm to get hard-to-predict names. */
+int
+__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+{
+ int (*tryfunc) (char *, void *);
+
+ switch (kind)
+ {
+ case __GT_FILE:
+ tryfunc = try_file;
+ break;
+
+ case __GT_DIR:
+ tryfunc = try_dir;
+ break;
+
+ case __GT_NOCREATE:
+ tryfunc = try_nocreate;
+ break;
+
+ default:
+ assert (! "invalid KIND in __gen_tempname");
+ abort ();
+ }
+ return __try_tempname (tmpl, suffixlen, &flags, tryfunc);
+}
diff --git a/lib/tempname.h b/lib/tempname.h
index bd46f93f9..a33f3da75 100644
--- a/lib/tempname.h
+++ b/lib/tempname.h
@@ -1,6 +1,6 @@
/* Create a temporary file or directory.
- Copyright (C) 2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -32,6 +32,10 @@
# define GT_NOCREATE 2
# endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Generate a temporary file name based on TMPL. TMPL must match the
rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
The name constructed does not exist at the time of the call to
@@ -47,4 +51,15 @@
We use a clever algorithm to get hard-to-predict names. */
extern int gen_tempname (char *tmpl, int suffixlen, int flags, int kind);
+/* Similar to gen_tempname, but TRYFUNC is called for each temporary
+ name to try. If TRYFUNC returns a non-negative number, TRY_GEN_TEMPNAME
+ returns with this value. Otherwise, if errno is set to EEXIST, another
+ name is tried, or else TRY_GEN_TEMPNAME returns -1. */
+extern int try_tempname (char *tmpl, int suffixlen, void *args,
+ int (*tryfunc) (char *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
#endif /* GL_TEMPNAME_H */
diff --git a/lib/time-internal.h b/lib/time-internal.h
new file mode 100644
index 000000000..7137a6716
--- /dev/null
+++ b/lib/time-internal.h
@@ -0,0 +1,49 @@
+/* Time internal interface
+
+ Copyright 2015 Free Software Foundation, Inc.
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 program; if not, see . */
+
+/* Written by Paul Eggert. */
+
+/* A time zone rule. */
+struct tm_zone
+{
+ /* More abbreviations, should they be needed. Their TZ_IS_SET
+ members are zero. */
+ struct tm_zone *next;
+
+#if HAVE_TZNAME && !HAVE_TM_ZONE
+ /* Copies of recent strings taken from tzname[0] and tzname[1].
+ The copies are in ABBRS, so that they survive tzset. Null if unknown. */
+ char *tzname_copy[2];
+#endif
+
+ /* If nonzero, the rule represents the TZ environment variable set
+ to the first "abbreviation" (this may be the empty string).
+ Otherwise, it represents an unset TZ. */
+ char tz_is_set;
+
+ /* A sequence of null-terminated strings packed next to each other.
+ The strings are followed by an extra null byte. If TZ_IS_SET,
+ there must be at least one string and the first string (which is
+ actually a TZ environment value value) may be empty. Otherwise
+ all strings must be nonempty.
+
+ Abbreviations are stored here because otherwise the values of
+ tm_zone and/or tzname would be dead after changing TZ and calling
+ tzset. Abbreviations never move once allocated, and are live
+ until tzfree is called. */
+ char abbrs[FLEXIBLE_ARRAY_MEMBER];
+};
diff --git a/lib/time.in.h b/lib/time.in.h
index 01681cc8c..7df4a6085 100644
--- a/lib/time.in.h
+++ b/lib/time.in.h
@@ -1,6 +1,6 @@
/* A more-standard .
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -22,11 +22,13 @@
/* Don't get in the way of glibc when it includes time.h merely to
declare a few standard symbols, rather than to declare all the
- symbols. Also, Solaris 8 eventually includes itself
+ symbols. (However, skip this for MinGW as it treats __need_time_t
+ incompatibly.) Also, Solaris 8 eventually includes itself
recursively; if that is happening, just include the system
without adding our own declarations. */
-#if (defined __need_time_t || defined __need_clock_t \
- || defined __need_timespec \
+#if (((defined __need_time_t || defined __need_clock_t \
+ || defined __need_timespec) \
+ && !defined __MINGW32__) \
|| defined _@GUARD_PREFIX@_TIME_H)
# @INCLUDE_NEXT@ @NEXT_TIME_H@
@@ -55,6 +57,8 @@
# include
# elif @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
# include
+# elif @UNISTD_H_DEFINES_STRUCT_TIMESPEC@
+# include
# else
# ifdef __cplusplus
@@ -229,6 +233,25 @@ _GL_CXXALIAS_SYS (strptime, char *, (char const *restrict __buf,
_GL_CXXALIASWARN (strptime);
# endif
+# if defined _GNU_SOURCE && @GNULIB_TIME_RZ@ && ! @HAVE_TIMEZONE_T@
+typedef struct tm_zone *timezone_t;
+_GL_FUNCDECL_SYS (tzalloc, timezone_t, (char const *__name));
+_GL_CXXALIAS_SYS (tzalloc, timezone_t, (char const *__name));
+_GL_FUNCDECL_SYS (tzfree, void, (timezone_t __tz));
+_GL_CXXALIAS_SYS (tzfree, void, (timezone_t __tz));
+_GL_FUNCDECL_SYS (localtime_rz, struct tm *,
+ (timezone_t __tz, time_t const *restrict __timer,
+ struct tm *restrict __result) _GL_ARG_NONNULL ((2, 3)));
+_GL_CXXALIAS_SYS (localtime_rz, struct tm *,
+ (timezone_t __tz, time_t const *restrict __timer,
+ struct tm *restrict __result));
+_GL_FUNCDECL_SYS (mktime_z, time_t,
+ (timezone_t __tz, struct tm *restrict __result)
+ _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_SYS (mktime_z, time_t,
+ (timezone_t __tz, struct tm *restrict __result));
+# endif
+
/* Convert TM to a time_t value, assuming UTC. */
# if @GNULIB_TIMEGM@
# if @REPLACE_TIMEGM@
diff --git a/lib/time_r.c b/lib/time_r.c
index 0249750e8..2db2bc075 100644
--- a/lib/time_r.c
+++ b/lib/time_r.c
@@ -1,6 +1,6 @@
/* Reentrant time functions like localtime_r.
- Copyright (C) 2003, 2006-2007, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2006-2007, 2010-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/time_rz.c b/lib/time_rz.c
new file mode 100644
index 000000000..888eed227
--- /dev/null
+++ b/lib/time_rz.c
@@ -0,0 +1,323 @@
+/* Time zone functions such as tzalloc and localtime_rz
+
+ Copyright 2015 Free Software Foundation, Inc.
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 program; if not, see . */
+
+/* Written by Paul Eggert. */
+
+/* Although this module is not thread-safe, any races should be fairly
+ rare and reasonably benign. For complete thread-safety, use a C
+ library with a working timezone_t type, so that this module is not
+ needed. */
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "time-internal.h"
+
+#if !HAVE_TZSET
+static void tzset (void) { }
+#endif
+
+/* The approximate size to use for small allocation requests. This is
+ the largest "small" request for the GNU C library malloc. */
+enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 };
+
+/* Minimum size of the ABBRS member of struct abbr. ABBRS is larger
+ only in the unlikely case where an abbreviation longer than this is
+ used. */
+enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) };
+
+static char const TZ[] = "TZ";
+
+/* Magic cookie timezone_t value, for local time. It differs from
+ NULL and from all other timezone_t values. Only the address
+ matters; the pointer is never dereferenced. */
+static timezone_t const local_tz = (timezone_t) 1;
+
+#if HAVE_TM_ZONE || HAVE_TZNAME
+
+/* Return true if the values A and B differ according to the rules for
+ tm_isdst: A and B differ if one is zero and the other positive. */
+static bool
+isdst_differ (int a, int b)
+{
+ return !a != !b && 0 <= a && 0 <= b;
+}
+
+/* Return true if A and B are equal. */
+static int
+equal_tm (const struct tm *a, const struct tm *b)
+{
+ return ! ((a->tm_sec ^ b->tm_sec)
+ | (a->tm_min ^ b->tm_min)
+ | (a->tm_hour ^ b->tm_hour)
+ | (a->tm_mday ^ b->tm_mday)
+ | (a->tm_mon ^ b->tm_mon)
+ | (a->tm_year ^ b->tm_year)
+ | isdst_differ (a->tm_isdst, b->tm_isdst));
+}
+
+#endif
+
+/* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this
+ includes its trailing null byte). Append an extra null byte to
+ mark the end of ABBRS. */
+static void
+extend_abbrs (char *abbrs, char const *abbr, size_t abbr_size)
+{
+ memcpy (abbrs, abbr, abbr_size);
+ abbrs[abbr_size] = '\0';
+}
+
+/* Return a newly allocated time zone for NAME, or NULL on failure.
+ A null NAME stands for wall clock time (which is like unset TZ). */
+timezone_t
+tzalloc (char const *name)
+{
+ size_t name_size = name ? strlen (name) + 1 : 0;
+ size_t abbr_size = name_size < ABBR_SIZE_MIN ? ABBR_SIZE_MIN : name_size + 1;
+ timezone_t tz = malloc (offsetof (struct tm_zone, abbrs) + abbr_size);
+ if (tz)
+ {
+ tz->next = NULL;
+#if HAVE_TZNAME && !HAVE_TM_ZONE
+ tz->tzname_copy[0] = tz->tzname_copy[1] = NULL;
+#endif
+ tz->tz_is_set = !!name;
+ tz->abbrs[0] = '\0';
+ if (name)
+ extend_abbrs (tz->abbrs, name, name_size);
+ }
+ return tz;
+}
+
+/* Save into TZ any nontrivial time zone abbreviation used by TM, and
+ update *TM (if HAVE_TM_ZONE) or *TZ (if !HAVE_TM_ZONE &&
+ HAVE_TZNAME) if they use the abbreviation. Return true if
+ successful, false (setting errno) otherwise. */
+static bool
+save_abbr (timezone_t tz, struct tm *tm)
+{
+#if HAVE_TM_ZONE || HAVE_TZNAME
+ char const *zone = NULL;
+ char *zone_copy = (char *) "";
+
+# if HAVE_TZNAME
+ int tzname_index = -1;
+# endif
+
+# if HAVE_TM_ZONE
+ zone = tm->tm_zone;
+# endif
+
+# if HAVE_TZNAME
+ if (! (zone && *zone) && 0 <= tm->tm_isdst)
+ {
+ tzname_index = tm->tm_isdst != 0;
+ zone = tzname[tzname_index];
+ }
+# endif
+
+ /* No need to replace null zones, or zones within the struct tm. */
+ if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1)))
+ return true;
+
+ if (*zone)
+ {
+ zone_copy = tz->abbrs;
+
+ while (strcmp (zone_copy, zone) != 0)
+ {
+ if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set)))
+ {
+ size_t zone_size = strlen (zone) + 1;
+ if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy)
+ extend_abbrs (zone_copy, zone, zone_size);
+ else
+ {
+ tz = tz->next = tzalloc (zone);
+ if (!tz)
+ return false;
+ tz->tz_is_set = 0;
+ zone_copy = tz->abbrs;
+ }
+ break;
+ }
+
+ zone_copy += strlen (zone_copy) + 1;
+ if (!*zone_copy && tz->next)
+ {
+ tz = tz->next;
+ zone_copy = tz->abbrs;
+ }
+ }
+ }
+
+ /* Replace the zone name so that its lifetime matches that of TZ. */
+# if HAVE_TM_ZONE
+ tm->tm_zone = zone_copy;
+# else
+ if (0 <= tzname_index)
+ tz->tzname_copy[tzname_index] = zone_copy;
+# endif
+#endif
+
+ return true;
+}
+
+/* Free a time zone. */
+void
+tzfree (timezone_t tz)
+{
+ if (tz != local_tz)
+ while (tz)
+ {
+ timezone_t next = tz->next;
+ free (tz);
+ tz = next;
+ }
+}
+
+/* Get and set the TZ environment variable. These functions can be
+ overridden by programs like Emacs that manage their own environment. */
+
+#ifndef getenv_TZ
+static char *
+getenv_TZ (void)
+{
+ return getenv (TZ);
+}
+#endif
+
+#ifndef setenv_TZ
+static int
+setenv_TZ (char const *tz)
+{
+ return tz ? setenv (TZ, tz, 1) : unsetenv (TZ);
+}
+#endif
+
+/* Change the environment to match the specified timezone_t value.
+ Return true if successful, false (setting errno) otherwise. */
+static bool
+change_env (timezone_t tz)
+{
+ if (setenv_TZ (tz->tz_is_set ? tz->abbrs : NULL) != 0)
+ return false;
+ tzset ();
+ return true;
+}
+
+/* Temporarily set the time zone to TZ, which must not be null.
+ Return LOCAL_TZ if the time zone setting is already correct.
+ Otherwise return a newly allocated time zone representing the old
+ setting, or NULL (setting errno) on failure. */
+static timezone_t
+set_tz (timezone_t tz)
+{
+ char *env_tz = getenv_TZ ();
+ if (env_tz
+ ? tz->tz_is_set && strcmp (tz->abbrs, env_tz) == 0
+ : !tz->tz_is_set)
+ return local_tz;
+ else
+ {
+ timezone_t old_tz = tzalloc (env_tz);
+ if (!old_tz)
+ return old_tz;
+ if (! change_env (tz))
+ {
+ int saved_errno = errno;
+ tzfree (old_tz);
+ errno = saved_errno;
+ return NULL;
+ }
+ return old_tz;
+ }
+}
+
+/* Restore an old setting returned by set_tz. It must not be null.
+ Return true (preserving errno) if successful, false (setting errno)
+ otherwise. */
+static bool
+revert_tz (timezone_t tz)
+{
+ if (tz == local_tz)
+ return true;
+ else
+ {
+ int saved_errno = errno;
+ bool ok = change_env (tz);
+ if (!ok)
+ saved_errno = errno;
+ tzfree (tz);
+ errno = saved_errno;
+ return ok;
+ }
+}
+
+/* Use time zone TZ to compute localtime_r (T, TM). */
+struct tm *
+localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
+{
+ if (!tz)
+ return gmtime_r (t, tm);
+ else
+ {
+ timezone_t old_tz = set_tz (tz);
+ if (old_tz)
+ {
+ bool abbr_saved = localtime_r (t, tm) && save_abbr (tz, tm);
+ if (revert_tz (old_tz) && abbr_saved)
+ return tm;
+ }
+ return NULL;
+ }
+}
+
+/* Use time zone TZ to compute mktime (TM). */
+time_t
+mktime_z (timezone_t tz, struct tm *tm)
+{
+ if (!tz)
+ return timegm (tm);
+ else
+ {
+ timezone_t old_tz = set_tz (tz);
+ if (old_tz)
+ {
+ time_t t = mktime (tm);
+#if HAVE_TM_ZONE || HAVE_TZNAME
+ time_t badtime = -1;
+ struct tm tm_1;
+ if ((t != badtime
+ || (localtime_r (&t, &tm_1) && equal_tm (tm, &tm_1)))
+ && !save_abbr (tz, tm))
+ t = badtime;
+#endif
+ if (revert_tz (old_tz))
+ return t;
+ }
+ return -1;
+ }
+}
diff --git a/lib/timegm.c b/lib/timegm.c
new file mode 100644
index 000000000..d1b355f2f
--- /dev/null
+++ b/lib/timegm.c
@@ -0,0 +1,38 @@
+/* Convert UTC calendar time to simple time. Like mktime but assumes UTC.
+
+ Copyright (C) 1994, 1997, 2003-2004, 2006-2007, 2009-2015 Free Software
+ Foundation, Inc. This file is part of the GNU C Library.
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 program; if not, see . */
+
+#ifndef _LIBC
+# include
+#endif
+
+#include
+
+#ifndef _LIBC
+# undef __gmtime_r
+# define __gmtime_r gmtime_r
+# define __mktime_internal mktime_internal
+# include "mktime-internal.h"
+#endif
+
+time_t
+timegm (struct tm *tmp)
+{
+ static time_t gmtime_offset;
+ tmp->tm_isdst = 0;
+ return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
+}
diff --git a/lib/times.c b/lib/times.c
index 605f2356f..3cfad2f09 100644
--- a/lib/times.c
+++ b/lib/times.c
@@ -1,6 +1,6 @@
/* Get process times
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -62,5 +62,5 @@ times (struct tms * buffer)
buffer->tms_cutime = 0;
buffer->tms_cstime = 0;
- return filetime2clock (creation_time);
+ return clock ();
}
diff --git a/lib/trunc.c b/lib/trunc.c
index e2857335b..b7b0aa2d0 100644
--- a/lib/trunc.c
+++ b/lib/trunc.c
@@ -1,5 +1,5 @@
/* Round towards zero.
- Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/unistd.c b/lib/unistd.c
index 6c6a8e268..72bad1c05 100644
--- a/lib/unistd.c
+++ b/lib/unistd.c
@@ -1,3 +1,4 @@
#include
#define _GL_UNISTD_INLINE _GL_EXTERN_INLINE
#include "unistd.h"
+typedef int dummy;
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index 842025024..c0bc8c7fe 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -1,5 +1,5 @@
/* Substitute for and wrapper around .
- Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -401,6 +401,12 @@ _GL_WARN_ON_USE (dup3, "dup3 is unportable - "
/* Set of environment variables and values. An array of strings of the form
"VARIABLE=VALUE", terminated with a NULL. */
# if defined __APPLE__ && defined __MACH__
+# include
+# if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+# define _GL_USE_CRT_EXTERNS
+# endif
+# endif
+# ifdef _GL_USE_CRT_EXTERNS
# include
# define environ (*_NSGetEnviron ())
# else
@@ -1287,13 +1293,24 @@ _GL_WARN_ON_USE (readlink, "readlink is unportable - "
#if @GNULIB_READLINKAT@
-# if !@HAVE_READLINKAT@
+# if @REPLACE_READLINKAT@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# define readlinkat rpl_readlinkat
+# endif
+_GL_FUNCDECL_RPL (readlinkat, ssize_t,
+ (int fd, char const *file, char *buf, size_t len)
+ _GL_ARG_NONNULL ((2, 3)));
+_GL_CXXALIAS_RPL (readlinkat, ssize_t,
+ (int fd, char const *file, char *buf, size_t len));
+# else
+# if !@HAVE_READLINKAT@
_GL_FUNCDECL_SYS (readlinkat, ssize_t,
(int fd, char const *file, char *buf, size_t len)
_GL_ARG_NONNULL ((2, 3)));
-# endif
+# endif
_GL_CXXALIAS_SYS (readlinkat, ssize_t,
(int fd, char const *file, char *buf, size_t len));
+# endif
_GL_CXXALIASWARN (readlinkat);
#elif defined GNULIB_POSIXCHECK
# undef readlinkat
@@ -1407,13 +1424,25 @@ _GL_WARN_ON_USE (symlink, "symlink is not portable - "
#if @GNULIB_SYMLINKAT@
-# if !@HAVE_SYMLINKAT@
+# if @REPLACE_SYMLINKAT@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef symlinkat
+# define symlinkat rpl_symlinkat
+# endif
+_GL_FUNCDECL_RPL (symlinkat, int,
+ (char const *contents, int fd, char const *file)
+ _GL_ARG_NONNULL ((1, 3)));
+_GL_CXXALIAS_RPL (symlinkat, int,
+ (char const *contents, int fd, char const *file));
+# else
+# if !@HAVE_SYMLINKAT@
_GL_FUNCDECL_SYS (symlinkat, int,
(char const *contents, int fd, char const *file)
_GL_ARG_NONNULL ((1, 3)));
-# endif
+# endif
_GL_CXXALIAS_SYS (symlinkat, int,
(char const *contents, int fd, char const *file));
+# endif
_GL_CXXALIASWARN (symlinkat);
#elif defined GNULIB_POSIXCHECK
# undef symlinkat
diff --git a/lib/unistr.in.h b/lib/unistr.in.h
index 73d2c23c0..90441493f 100644
--- a/lib/unistr.in.h
+++ b/lib/unistr.in.h
@@ -1,5 +1,5 @@
/* Elementary Unicode string functions.
- Copyright (C) 2001-2002, 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2005-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/unistr/u8-mbtouc-aux.c b/lib/unistr/u8-mbtouc-aux.c
index 02cdacd9d..834725fed 100644
--- a/lib/unistr/u8-mbtouc-aux.c
+++ b/lib/unistr/u8-mbtouc-aux.c
@@ -1,5 +1,5 @@
/* Conversion UTF-8 to UCS-4.
- Copyright (C) 2001-2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-mbtouc-unsafe-aux.c b/lib/unistr/u8-mbtouc-unsafe-aux.c
index bfa96f4ab..b406d3ebd 100644
--- a/lib/unistr/u8-mbtouc-unsafe-aux.c
+++ b/lib/unistr/u8-mbtouc-unsafe-aux.c
@@ -1,5 +1,5 @@
/* Conversion UTF-8 to UCS-4.
- Copyright (C) 2001-2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-mbtouc-unsafe.c b/lib/unistr/u8-mbtouc-unsafe.c
index 9c2095b68..01d12dc41 100644
--- a/lib/unistr/u8-mbtouc-unsafe.c
+++ b/lib/unistr/u8-mbtouc-unsafe.c
@@ -1,5 +1,5 @@
/* Look at first character in UTF-8 string.
- Copyright (C) 1999-2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-mbtouc.c b/lib/unistr/u8-mbtouc.c
index 2b389deb7..dc4607f68 100644
--- a/lib/unistr/u8-mbtouc.c
+++ b/lib/unistr/u8-mbtouc.c
@@ -1,5 +1,5 @@
/* Look at first character in UTF-8 string.
- Copyright (C) 1999-2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-mbtoucr.c b/lib/unistr/u8-mbtoucr.c
index 3a75a4118..3d8c05f76 100644
--- a/lib/unistr/u8-mbtoucr.c
+++ b/lib/unistr/u8-mbtoucr.c
@@ -1,5 +1,5 @@
/* Look at first character in UTF-8 string, returning an error code.
- Copyright (C) 1999-2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999-2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2001.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-prev.c b/lib/unistr/u8-prev.c
index b2c2b9b41..e01551e57 100644
--- a/lib/unistr/u8-prev.c
+++ b/lib/unistr/u8-prev.c
@@ -1,5 +1,5 @@
/* Iterate over previous character in UTF-8 string.
- Copyright (C) 2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2002.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-uctomb-aux.c b/lib/unistr/u8-uctomb-aux.c
index 8d94bf57a..cc9c5441c 100644
--- a/lib/unistr/u8-uctomb-aux.c
+++ b/lib/unistr/u8-uctomb-aux.c
@@ -1,5 +1,5 @@
/* Conversion UCS-4 to UTF-8.
- Copyright (C) 2002, 2006-2007, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2006-2007, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2002.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unistr/u8-uctomb.c b/lib/unistr/u8-uctomb.c
index 1ce271fe8..43ef23d89 100644
--- a/lib/unistr/u8-uctomb.c
+++ b/lib/unistr/u8-uctomb.c
@@ -1,5 +1,5 @@
/* Store a character in UTF-8 string.
- Copyright (C) 2002, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2005-2006, 2009-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2002.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/unitypes.in.h b/lib/unitypes.in.h
index e5ff9923c..fb0ce68a1 100644
--- a/lib/unitypes.in.h
+++ b/lib/unitypes.in.h
@@ -1,5 +1,5 @@
/* Elementary types and macros for the GNU UniString library.
- Copyright (C) 2002, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/unsetenv.c b/lib/unsetenv.c
new file mode 100644
index 000000000..5bd9ab493
--- /dev/null
+++ b/lib/unsetenv.c
@@ -0,0 +1,127 @@
+/* Copyright (C) 1992, 1995-2002, 2005-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program 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 program 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 program. If not, see . */
+
+/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
+ optimizes away the name == NULL test below. */
+#define _GL_ARG_NONNULL(params)
+
+#include
+
+/* Specification. */
+#include
+
+#include
+#if !_LIBC
+# define __set_errno(ev) ((errno) = (ev))
+#endif
+
+#include
+#include
+
+#if !_LIBC
+# define __environ environ
+#endif
+
+#if _LIBC
+/* This lock protects against simultaneous modifications of 'environ'. */
+# include
+__libc_lock_define_initialized (static, envlock)
+# define LOCK __libc_lock_lock (envlock)
+# define UNLOCK __libc_lock_unlock (envlock)
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+/* In the GNU C library we must keep the namespace clean. */
+#ifdef _LIBC
+# define unsetenv __unsetenv
+#endif
+
+#if _LIBC || !HAVE_UNSETENV
+
+int
+unsetenv (const char *name)
+{
+ size_t len;
+ char **ep;
+
+ if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ len = strlen (name);
+
+ LOCK;
+
+ ep = __environ;
+ while (*ep != NULL)
+ if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
+ {
+ /* Found it. Remove this pointer by moving later ones back. */
+ char **dp = ep;
+
+ do
+ dp[0] = dp[1];
+ while (*dp++);
+ /* Continue the loop in case NAME appears again. */
+ }
+ else
+ ++ep;
+
+ UNLOCK;
+
+ return 0;
+}
+
+#ifdef _LIBC
+# undef unsetenv
+weak_alias (__unsetenv, unsetenv)
+#endif
+
+#else /* HAVE_UNSETENV */
+
+# undef unsetenv
+# if !HAVE_DECL_UNSETENV
+# if VOID_UNSETENV
+extern void unsetenv (const char *);
+# else
+extern int unsetenv (const char *);
+# endif
+# endif
+
+/* Call the underlying unsetenv, in case there is hidden bookkeeping
+ that needs updating beyond just modifying environ. */
+int
+rpl_unsetenv (const char *name)
+{
+ int result = 0;
+ if (!name || !*name || strchr (name, '='))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ while (getenv (name))
+# if !VOID_UNSETENV
+ result =
+# endif
+ unsetenv (name);
+ return result;
+}
+
+#endif /* HAVE_UNSETENV */
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
index 7282b0504..daea81642 100644
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -1,5 +1,5 @@
/* vsprintf with automatic memory allocation.
- Copyright (C) 1999, 2002-2014 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2002-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -1886,7 +1886,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
else
{
do
- result[length++] = (unsigned char) *cp++;
+ result[length++] = *cp++;
while (--n > 0);
}
}
@@ -1957,15 +1957,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
if (arg < 0)
{
/* "A negative field width is taken as a '-' flag
followed by a positive field width." */
flags |= FLAG_LEFT;
- width = (unsigned int) (-arg);
+ width = -width;
}
- else
- width = arg;
}
else
{
@@ -2073,8 +2072,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
characters = 0;
}
- if (has_width && width > characters
- && !(dp->flags & FLAG_LEFT))
+ if (characters < width && !(dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2127,8 +2125,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
# endif
- if (has_width && width > characters
- && (dp->flags & FLAG_LEFT))
+ if (characters < width && (dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2201,8 +2198,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
characters = 0;
}
- if (has_width && width > characters
- && !(dp->flags & FLAG_LEFT))
+ if (characters < width && !(dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2255,8 +2251,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
# endif
- if (has_width && width > characters
- && (dp->flags & FLAG_LEFT))
+ if (characters < width && (dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2329,8 +2324,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
characters = 0;
}
- if (has_width && width > characters
- && !(dp->flags & FLAG_LEFT))
+ if (characters < width && !(dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2383,8 +2377,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
# endif
- if (has_width && width > characters
- && (dp->flags & FLAG_LEFT))
+ if (characters < width && (dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2435,15 +2428,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
if (arg < 0)
{
/* "A negative field width is taken as a '-' flag
followed by a positive field width." */
flags |= FLAG_LEFT;
- width = (unsigned int) (-arg);
+ width = -width;
}
- else
- width = arg;
}
else
{
@@ -2573,8 +2565,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
characters = 0;
}
- if (has_width && width > characters
- && !(dp->flags & FLAG_LEFT))
+ if (characters < width && !(dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2635,8 +2626,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
}
- if (has_width && width > characters
- && (dp->flags & FLAG_LEFT))
+ if (characters < width && (dp->flags & FLAG_LEFT))
{
size_t n = width - characters;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2827,8 +2817,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* w doesn't matter. */
w = 0;
- if (has_width && width > w
- && !(dp->flags & FLAG_LEFT))
+ if (w < width && !(dp->flags & FLAG_LEFT))
{
size_t n = width - w;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2911,8 +2900,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
length += tmpdst_len;
# endif
- if (has_width && width > w
- && (dp->flags & FLAG_LEFT))
+ if (w < width && (dp->flags & FLAG_LEFT))
{
size_t n = width - w;
ENSURE_ALLOCATION (xsum (length, n));
@@ -2939,17 +2927,16 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
arg_type type = a.arg[dp->arg_index].type;
int flags = dp->flags;
- int has_width;
size_t width;
int has_precision;
size_t precision;
size_t tmp_length;
+ size_t count;
DCHAR_T tmpbuf[700];
DCHAR_T *tmp;
DCHAR_T *pad_ptr;
DCHAR_T *p;
- has_width = 0;
width = 0;
if (dp->width_start != dp->width_end)
{
@@ -2960,15 +2947,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
if (arg < 0)
{
/* "A negative field width is taken as a '-' flag
followed by a positive field width." */
flags |= FLAG_LEFT;
- width = (unsigned int) (-arg);
+ width = -width;
}
- else
- width = arg;
}
else
{
@@ -2978,7 +2964,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
- has_width = 1;
}
has_precision = 0;
@@ -3354,11 +3339,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
abort ();
# endif
}
+
/* The generated string now extends from tmp to p, with the
zero padding insertion point being at pad_ptr. */
- if (has_width && p - tmp < width)
+ count = p - tmp;
+
+ if (count < width)
{
- size_t pad = width - (p - tmp);
+ size_t pad = width - count;
DCHAR_T *end = p + pad;
if (flags & FLAG_LEFT)
@@ -3391,28 +3379,26 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
p = end;
}
- {
- size_t count = p - tmp;
+ count = p - tmp;
- if (count >= tmp_length)
- /* tmp_length was incorrectly calculated - fix the
- code above! */
- abort ();
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
- /* Make room for the result. */
- if (count >= allocated - length)
- {
- size_t n = xsum (length, count);
+ /* Make room for the result. */
+ if (count >= allocated - length)
+ {
+ size_t n = xsum (length, count);
- ENSURE_ALLOCATION (n);
- }
+ ENSURE_ALLOCATION (n);
+ }
- /* Append the result. */
- memcpy (result + length, tmp, count * sizeof (DCHAR_T));
- if (tmp != tmpbuf)
- free (tmp);
- length += count;
- }
+ /* Append the result. */
+ memcpy (result + length, tmp, count * sizeof (DCHAR_T));
+ if (tmp != tmpbuf)
+ free (tmp);
+ length += count;
}
#endif
#if (NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL
@@ -3446,8 +3432,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
arg_type type = a.arg[dp->arg_index].type;
# endif
int flags = dp->flags;
- int has_width;
size_t width;
+ size_t count;
int has_precision;
size_t precision;
size_t tmp_length;
@@ -3456,7 +3442,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
DCHAR_T *pad_ptr;
DCHAR_T *p;
- has_width = 0;
width = 0;
if (dp->width_start != dp->width_end)
{
@@ -3467,15 +3452,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
if (arg < 0)
{
/* "A negative field width is taken as a '-' flag
followed by a positive field width." */
flags |= FLAG_LEFT;
- width = (unsigned int) (-arg);
+ width = -width;
}
- else
- width = arg;
}
else
{
@@ -3485,7 +3469,6 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
- has_width = 1;
}
has_precision = 0;
@@ -3925,9 +3908,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
digits without trailing zeroes. */
if (exponent >= 0)
{
- size_t count = exponent + 1;
+ size_t ecount = exponent + 1;
/* Note: count <= precision = ndigits. */
- for (; count > 0; count--)
+ for (; ecount > 0; ecount--)
*p++ = digits[--ndigits];
if ((flags & FLAG_ALT) || ndigits > nzeroes)
{
@@ -3941,10 +3924,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
else
{
- size_t count = -exponent - 1;
+ size_t ecount = -exponent - 1;
*p++ = '0';
*p++ = decimal_point_char ();
- for (; count > 0; count--)
+ for (; ecount > 0; ecount--)
*p++ = '0';
while (ndigits > nzeroes)
{
@@ -4395,9 +4378,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
digits without trailing zeroes. */
if (exponent >= 0)
{
- size_t count = exponent + 1;
- /* Note: count <= precision = ndigits. */
- for (; count > 0; count--)
+ size_t ecount = exponent + 1;
+ /* Note: ecount <= precision = ndigits. */
+ for (; ecount > 0; ecount--)
*p++ = digits[--ndigits];
if ((flags & FLAG_ALT) || ndigits > nzeroes)
{
@@ -4411,10 +4394,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
}
else
{
- size_t count = -exponent - 1;
+ size_t ecount = -exponent - 1;
*p++ = '0';
*p++ = decimal_point_char ();
- for (; count > 0; count--)
+ for (; ecount > 0; ecount--)
*p++ = '0';
while (ndigits > nzeroes)
{
@@ -4542,9 +4525,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* The generated string now extends from tmp to p, with the
zero padding insertion point being at pad_ptr. */
- if (has_width && p - tmp < width)
+ count = p - tmp;
+
+ if (count < width)
{
- size_t pad = width - (p - tmp);
+ size_t pad = width - count;
DCHAR_T *end = p + pad;
if (flags & FLAG_LEFT)
@@ -4577,36 +4562,36 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
p = end;
}
- {
- size_t count = p - tmp;
+ count = p - tmp;
- if (count >= tmp_length)
- /* tmp_length was incorrectly calculated - fix the
- code above! */
- abort ();
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
- /* Make room for the result. */
- if (count >= allocated - length)
- {
- size_t n = xsum (length, count);
+ /* Make room for the result. */
+ if (count >= allocated - length)
+ {
+ size_t n = xsum (length, count);
- ENSURE_ALLOCATION (n);
- }
+ ENSURE_ALLOCATION (n);
+ }
- /* Append the result. */
- memcpy (result + length, tmp, count * sizeof (DCHAR_T));
- if (tmp != tmpbuf)
- free (tmp);
- length += count;
- }
+ /* Append the result. */
+ memcpy (result + length, tmp, count * sizeof (DCHAR_T));
+ if (tmp != tmpbuf)
+ free (tmp);
+ length += count;
}
#endif
else
{
arg_type type = a.arg[dp->arg_index].type;
int flags = dp->flags;
-#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
int has_width;
+#endif
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
size_t width;
#endif
#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || NEED_PRINTF_UNBOUNDED_PRECISION
@@ -4635,8 +4620,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
TCHAR_T *tmp;
#endif
-#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
has_width = 0;
+#endif
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
width = 0;
if (dp->width_start != dp->width_end)
{
@@ -4647,15 +4634,14 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
if (arg < 0)
{
/* "A negative field width is taken as a '-' flag
followed by a positive field width." */
flags |= FLAG_LEFT;
- width = (unsigned int) (-arg);
+ width = -width;
}
- else
- width = arg;
}
else
{
@@ -4665,7 +4651,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
width = xsum (xtimes (width, 10), *digitp++ - '0');
while (digitp != dp->width_end);
}
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
has_width = 1;
+#endif
}
#endif
@@ -4805,7 +4793,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
const FCHAR_T *mp = dp->width_start;
do
- *fbp++ = (unsigned char) *mp++;
+ *fbp++ = *mp++;
while (--n > 0);
}
}
@@ -4826,7 +4814,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
{
const FCHAR_T *mp = dp->precision_start;
do
- *fbp++ = (unsigned char) *mp++;
+ *fbp++ = *mp++;
while (--n > 0);
}
}
@@ -5153,7 +5141,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
size_t tmp_length =
MAX_ROOM_NEEDED (&a, dp->arg_index,
dp->conversion, type, flags,
- has_width ? width : 0,
+ width,
has_precision,
precision, pad_ourselves);
@@ -5191,18 +5179,21 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
/* SNPRINTF or sprintf failed. Save and use the errno
that it has set, if any. */
int saved_errno = errno;
+ if (saved_errno == 0)
+ {
+ if (dp->conversion == 'c' || dp->conversion == 's')
+ saved_errno = EILSEQ;
+ else
+ saved_errno = EINVAL;
+ }
if (!(result == resultbuf || result == NULL))
free (result);
if (buf_malloced != NULL)
free (buf_malloced);
CLEANUP ();
- errno =
- (saved_errno != 0
- ? saved_errno
- : (dp->conversion == 'c' || dp->conversion == 's'
- ? EILSEQ
- : EINVAL));
+
+ errno = saved_errno;
return NULL;
}
@@ -5391,7 +5382,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
tmpsrc += count;
tmpdst += count;
for (n = count; n > 0; n--)
- *--tmpdst = (unsigned char) *--tmpsrc;
+ *--tmpdst = *--tmpsrc;
}
}
#endif
diff --git a/lib/vasnprintf.h b/lib/vasnprintf.h
index a3f48e828..d691a5ffe 100644
--- a/lib/vasnprintf.h
+++ b/lib/vasnprintf.h
@@ -1,5 +1,5 @@
/* vsprintf with automatic memory allocation.
- Copyright (C) 2002-2004, 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2002-2004, 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/verify.h b/lib/verify.h
index 78d543f04..db52900e1 100644
--- a/lib/verify.h
+++ b/lib/verify.h
@@ -1,6 +1,6 @@
/* Compile-time assert-like macros.
- Copyright (C) 2005-2006, 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2009-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/vsnprintf.c b/lib/vsnprintf.c
index 26b1887b0..96024096d 100644
--- a/lib/vsnprintf.c
+++ b/lib/vsnprintf.c
@@ -1,5 +1,5 @@
/* Formatted output to strings.
- Copyright (C) 2004, 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2006-2015 Free Software Foundation, Inc.
Written by Simon Josefsson and Yoann Vandoorselaere .
This program is free software; you can redistribute it and/or modify
diff --git a/lib/w32sock.h b/lib/w32sock.h
index 3946d4945..072a7f53d 100644
--- a/lib/w32sock.h
+++ b/lib/w32sock.h
@@ -1,6 +1,6 @@
/* w32sock.h --- internal auxiliary functions for Windows socket functions
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/wchar.in.h b/lib/wchar.in.h
index 1874b4d7e..b15ad4b71 100644
--- a/lib/wchar.in.h
+++ b/lib/wchar.in.h
@@ -1,6 +1,6 @@
/* A substitute for ISO C99 , for platforms that have issues.
- Copyright (C) 2007-2014 Free Software Foundation, Inc.
+ Copyright (C) 2007-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -30,9 +30,14 @@
#endif
@PRAGMA_COLUMNS@
-#if defined __need_mbstate_t || defined __need_wint_t || (defined __hpux && ((defined _INTTYPES_INCLUDED && !defined strtoimax) || defined _GL_JUST_INCLUDE_SYSTEM_WCHAR_H)) || defined _GL_ALREADY_INCLUDING_WCHAR_H
+#if (((defined __need_mbstate_t || defined __need_wint_t) \
+ && !defined __MINGW32__) \
+ || (defined __hpux \
+ && ((defined _INTTYPES_INCLUDED && !defined strtoimax) \
+ || defined _GL_JUST_INCLUDE_SYSTEM_WCHAR_H)) \
+ || defined _GL_ALREADY_INCLUDING_WCHAR_H)
/* Special invocation convention:
- - Inside glibc and uClibc header files.
+ - Inside glibc and uClibc header files, but not MinGW.
- On HP-UX 11.00 we have a sequence of nested includes
-> -> , and the latter includes ,
once indirectly -> -> ->
diff --git a/lib/wcrtomb.c b/lib/wcrtomb.c
index ebbdddccc..bd7a7babb 100644
--- a/lib/wcrtomb.c
+++ b/lib/wcrtomb.c
@@ -1,5 +1,5 @@
/* Convert wide character to multibyte character.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/wctype.in.h b/lib/wctype.in.h
index b5b6093d7..2c6fe4a79 100644
--- a/lib/wctype.in.h
+++ b/lib/wctype.in.h
@@ -1,6 +1,6 @@
/* A substitute for ISO C99 , for platforms that lack it.
- Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Copyright (C) 2006-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/write.c b/lib/write.c
index 51cc1d91e..dbc384008 100644
--- a/lib/write.c
+++ b/lib/write.c
@@ -1,5 +1,5 @@
/* POSIX compatible write() function.
- Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2008-2015 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/xsize.h b/lib/xsize.h
index 83cb960b5..a34d3435d 100644
--- a/lib/xsize.h
+++ b/lib/xsize.h
@@ -1,6 +1,6 @@
/* xsize.h -- Checked size_t computations.
- Copyright (C) 2003, 2008-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2008-2015 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/m4/00gnulib.m4 b/m4/00gnulib.m4
index 8eca5518a..c7103ed52 100644
--- a/m4/00gnulib.m4
+++ b/m4/00gnulib.m4
@@ -1,5 +1,5 @@
# 00gnulib.m4 serial 3
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/absolute-header.m4 b/m4/absolute-header.m4
index ce3e39e9b..bc19dfc30 100644
--- a/m4/absolute-header.m4
+++ b/m4/absolute-header.m4
@@ -1,5 +1,5 @@
# absolute-header.m4 serial 16
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/alloca.m4 b/m4/alloca.m4
index d7bdea631..8408bed28 100644
--- a/m4/alloca.m4
+++ b/m4/alloca.m4
@@ -1,5 +1,5 @@
# alloca.m4 serial 14
-dnl Copyright (C) 2002-2004, 2006-2007, 2009-2014 Free Software Foundation,
+dnl Copyright (C) 2002-2004, 2006-2007, 2009-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/arpa_inet_h.m4 b/m4/arpa_inet_h.m4
index f01699a9d..056bc7b4b 100644
--- a/m4/arpa_inet_h.m4
+++ b/m4/arpa_inet_h.m4
@@ -1,5 +1,5 @@
# arpa_inet_h.m4 serial 13
-dnl Copyright (C) 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/autobuild.m4 b/m4/autobuild.m4
index 00d870930..239809cd5 100644
--- a/m4/autobuild.m4
+++ b/m4/autobuild.m4
@@ -1,5 +1,5 @@
# autobuild.m4 serial 7
-dnl Copyright (C) 2004, 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2004, 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/btowc.m4 b/m4/btowc.m4
index 99889445f..c1da65d43 100644
--- a/m4/btowc.m4
+++ b/m4/btowc.m4
@@ -1,5 +1,5 @@
# btowc.m4 serial 10
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/byteswap.m4 b/m4/byteswap.m4
index 6d6357cbe..ec5d46cde 100644
--- a/m4/byteswap.m4
+++ b/m4/byteswap.m4
@@ -1,5 +1,5 @@
# byteswap.m4 serial 4
-dnl Copyright (C) 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005, 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/canonicalize.m4 b/m4/canonicalize.m4
index ace455661..6d932fd1a 100644
--- a/m4/canonicalize.m4
+++ b/m4/canonicalize.m4
@@ -1,6 +1,6 @@
# canonicalize.m4 serial 26
-dnl Copyright (C) 2003-2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003-2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/ceil.m4 b/m4/ceil.m4
index 128353ae7..fb5f6f0d5 100644
--- a/m4/ceil.m4
+++ b/m4/ceil.m4
@@ -1,5 +1,5 @@
# ceil.m4 serial 9
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/check-math-lib.m4 b/m4/check-math-lib.m4
index a3894aa64..1c83f8fcb 100644
--- a/m4/check-math-lib.m4
+++ b/m4/check-math-lib.m4
@@ -1,5 +1,5 @@
# check-math-lib.m4 serial 4
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/clock_time.m4 b/m4/clock_time.m4
index be36a42b8..efb2ac583 100644
--- a/m4/clock_time.m4
+++ b/m4/clock_time.m4
@@ -1,5 +1,5 @@
# clock_time.m4 serial 10
-dnl Copyright (C) 2002-2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/close.m4 b/m4/close.m4
index 68510c5c5..d04aefbee 100644
--- a/m4/close.m4
+++ b/m4/close.m4
@@ -1,5 +1,5 @@
# close.m4 serial 8
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/configmake.m4 b/m4/configmake.m4
index 0cd86cf99..49d3a13e3 100644
--- a/m4/configmake.m4
+++ b/m4/configmake.m4
@@ -1,5 +1,5 @@
# configmake.m4 serial 2
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/copysign.m4 b/m4/copysign.m4
index 1bb2d6fb9..7cb33b666 100644
--- a/m4/copysign.m4
+++ b/m4/copysign.m4
@@ -1,5 +1,5 @@
# copysign.m4 serial 1
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4
index 3f2b16b12..7abd1d05e 100644
--- a/m4/dirent_h.m4
+++ b/m4/dirent_h.m4
@@ -1,5 +1,5 @@
# dirent_h.m4 serial 16
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/dirfd.m4 b/m4/dirfd.m4
index b42276948..ce56cff69 100644
--- a/m4/dirfd.m4
+++ b/m4/dirfd.m4
@@ -2,7 +2,7 @@
dnl Find out how to get the file descriptor associated with an open DIR*.
-# Copyright (C) 2001-2006, 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2006, 2008-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/dirname.m4 b/m4/dirname.m4
index d2627b8a8..2a0be516e 100644
--- a/m4/dirname.m4
+++ b/m4/dirname.m4
@@ -1,5 +1,5 @@
#serial 10 -*- autoconf -*-
-dnl Copyright (C) 2002-2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/double-slash-root.m4 b/m4/double-slash-root.m4
index 937f4bca9..f307eb2ff 100644
--- a/m4/double-slash-root.m4
+++ b/m4/double-slash-root.m4
@@ -1,5 +1,5 @@
# double-slash-root.m4 serial 4 -*- Autoconf -*-
-dnl Copyright (C) 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/dup2.m4 b/m4/dup2.m4
index 89638a0bf..59028e098 100644
--- a/m4/dup2.m4
+++ b/m4/dup2.m4
@@ -1,5 +1,5 @@
-#serial 20
-dnl Copyright (C) 2002, 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+#serial 24
+dnl Copyright (C) 2002, 2005, 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -19,33 +19,50 @@ AC_DEFUN([gl_FUNC_DUP2],
if test $HAVE_DUP2 = 1; then
AC_CACHE_CHECK([whether dup2 works], [gl_cv_func_dup2_works],
[AC_RUN_IFELSE([
- AC_LANG_PROGRAM([[#include
-#include
-#include ]],
- [int result = 0;
-#ifdef FD_CLOEXEC
- if (fcntl (1, F_SETFD, FD_CLOEXEC) == -1)
- result |= 1;
-#endif
- if (dup2 (1, 1) == 0)
- result |= 2;
-#ifdef FD_CLOEXEC
- if (fcntl (1, F_GETFD) != FD_CLOEXEC)
- result |= 4;
-#endif
- close (0);
- if (dup2 (0, 0) != -1)
- result |= 8;
- /* Many gnulib modules require POSIX conformance of EBADF. */
- if (dup2 (2, 1000000) == -1 && errno != EBADF)
- result |= 16;
- /* Flush out some cygwin core dumps. */
- if (dup2 (2, -1) != -1 || errno != EBADF)
- result |= 32;
- dup2 (2, 255);
- dup2 (2, 256);
- return result;
- ])
+ AC_LANG_PROGRAM(
+ [[#include
+ #include
+ #include
+ #include
+ #include
+ #ifndef RLIM_SAVED_CUR
+ # define RLIM_SAVED_CUR RLIM_INFINITY
+ #endif
+ #ifndef RLIM_SAVED_MAX
+ # define RLIM_SAVED_MAX RLIM_INFINITY
+ #endif
+ ]],
+ [[int result = 0;
+ int bad_fd = INT_MAX;
+ struct rlimit rlim;
+ if (getrlimit (RLIMIT_NOFILE, &rlim) == 0
+ && 0 <= rlim.rlim_cur && rlim.rlim_cur <= INT_MAX
+ && rlim.rlim_cur != RLIM_INFINITY
+ && rlim.rlim_cur != RLIM_SAVED_MAX
+ && rlim.rlim_cur != RLIM_SAVED_CUR)
+ bad_fd = rlim.rlim_cur;
+ #ifdef FD_CLOEXEC
+ if (fcntl (1, F_SETFD, FD_CLOEXEC) == -1)
+ result |= 1;
+ #endif
+ if (dup2 (1, 1) != 1)
+ result |= 2;
+ #ifdef FD_CLOEXEC
+ if (fcntl (1, F_GETFD) != FD_CLOEXEC)
+ result |= 4;
+ #endif
+ close (0);
+ if (dup2 (0, 0) != -1)
+ result |= 8;
+ /* Many gnulib modules require POSIX conformance of EBADF. */
+ if (dup2 (2, bad_fd) == -1 && errno != EBADF)
+ result |= 16;
+ /* Flush out some cygwin core dumps. */
+ if (dup2 (2, -1) != -1 || errno != EBADF)
+ result |= 32;
+ dup2 (2, 255);
+ dup2 (2, 256);
+ return result;]])
],
[gl_cv_func_dup2_works=yes], [gl_cv_func_dup2_works=no],
[case "$host_os" in
@@ -53,13 +70,14 @@ AC_DEFUN([gl_FUNC_DUP2],
gl_cv_func_dup2_works="guessing no" ;;
cygwin*) # on cygwin 1.5.x, dup2(1,1) returns 0
gl_cv_func_dup2_works="guessing no" ;;
- linux*) # On linux between 2008-07-27 and 2009-05-11, dup2 of a
- # closed fd may yield -EBADF instead of -1 / errno=EBADF.
- gl_cv_func_dup2_works="guessing no" ;;
- freebsd*) # on FreeBSD 6.1, dup2(1,1000000) gives EMFILE, not EBADF.
+ aix* | freebsd*)
+ # on AIX 7.1 and FreeBSD 6.1, dup2 (1,toobig) gives EMFILE,
+ # not EBADF.
gl_cv_func_dup2_works="guessing no" ;;
haiku*) # on Haiku alpha 2, dup2(1, 1) resets FD_CLOEXEC.
gl_cv_func_dup2_works="guessing no" ;;
+ *-android*) # implemented using dup3(), which fails if oldfd == newfd
+ gl_cv_func_dup2_works="guessing no" ;;
*) gl_cv_func_dup2_works="guessing yes" ;;
esac])
])
diff --git a/m4/duplocale.m4 b/m4/duplocale.m4
index d45891d4f..5f17769cd 100644
--- a/m4/duplocale.m4
+++ b/m4/duplocale.m4
@@ -1,5 +1,5 @@
# duplocale.m4 serial 7
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/eealloc.m4 b/m4/eealloc.m4
index 8a51fe7c5..322bdd50b 100644
--- a/m4/eealloc.m4
+++ b/m4/eealloc.m4
@@ -1,5 +1,5 @@
# eealloc.m4 serial 3
-dnl Copyright (C) 2003, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/environ.m4 b/m4/environ.m4
index cfabe46f5..4dbf9473e 100644
--- a/m4/environ.m4
+++ b/m4/environ.m4
@@ -1,5 +1,5 @@
# environ.m4 serial 6
-dnl Copyright (C) 2001-2004, 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2001-2004, 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/errno_h.m4 b/m4/errno_h.m4
index 4ee9e6a14..cfaa68761 100644
--- a/m4/errno_h.m4
+++ b/m4/errno_h.m4
@@ -1,5 +1,5 @@
# errno_h.m4 serial 12
-dnl Copyright (C) 2004, 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2004, 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/exponentd.m4 b/m4/exponentd.m4
index 7bee63571..84f1691a6 100644
--- a/m4/exponentd.m4
+++ b/m4/exponentd.m4
@@ -1,5 +1,5 @@
# exponentd.m4 serial 3
-dnl Copyright (C) 2007-2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/exponentf.m4 b/m4/exponentf.m4
index b2dfeef96..95e32cdd9 100644
--- a/m4/exponentf.m4
+++ b/m4/exponentf.m4
@@ -1,5 +1,5 @@
# exponentf.m4 serial 2
-dnl Copyright (C) 2007-2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/exponentl.m4 b/m4/exponentl.m4
index d6f4ba7ff..6b95e7372 100644
--- a/m4/exponentl.m4
+++ b/m4/exponentl.m4
@@ -1,5 +1,5 @@
# exponentl.m4 serial 3
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/extensions.m4 b/m4/extensions.m4
index 37f55ca3d..35bc49c97 100644
--- a/m4/extensions.m4
+++ b/m4/extensions.m4
@@ -1,7 +1,7 @@
# serial 13 -*- Autoconf -*-
# Enable extensions on systems that normally disable them.
-# Copyright (C) 2003, 2006-2014 Free Software Foundation, Inc.
+# Copyright (C) 2003, 2006-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -68,6 +68,10 @@ dnl configure.ac when using autoheader 2.62.
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
+/* Use GNU style printf and scanf. */
+#ifndef __USE_MINGW_ANSI_STDIO
+# undef __USE_MINGW_ANSI_STDIO
+#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
@@ -100,6 +104,7 @@ dnl configure.ac when using autoheader 2.62.
AC_DEFINE([_ALL_SOURCE])
AC_DEFINE([_DARWIN_C_SOURCE])
AC_DEFINE([_GNU_SOURCE])
+ AC_DEFINE([__USE_MINGW_ANSI_STDIO])
AC_DEFINE([_POSIX_PTHREAD_SEMANTICS])
AC_DEFINE([_TANDEM_SOURCE])
AC_CACHE_CHECK([whether _XOPEN_SOURCE should be defined],
diff --git a/m4/extern-inline.m4 b/m4/extern-inline.m4
index 240150efb..72800650e 100644
--- a/m4/extern-inline.m4
+++ b/m4/extern-inline.m4
@@ -1,6 +1,6 @@
dnl 'extern inline' a la ISO C99.
-dnl Copyright 2012-2014 Free Software Foundation, Inc.
+dnl Copyright 2012-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -19,13 +19,28 @@ AC_DEFUN([gl_EXTERN_INLINE],
'reference to static identifier "f" in extern inline function'.
This bug was observed with Sun C 5.12 SunOS_i386 2011/11/16.
- Suppress the use of extern inline on problematic Apple configurations.
- OS X 10.8 and earlier mishandle it; see, e.g.,
- .
+ Suppress extern inline (with or without __attribute__ ((__gnu_inline__)))
+ on configurations that mistakenly use 'static inline' to implement
+ functions or macros in standard C headers like . For example,
+ if isdigit is mistakenly implemented via a static inline function,
+ a program containing an extern inline function that calls isdigit
+ may not work since the C standard prohibits extern inline functions
+ from calling static functions. This bug is known to occur on:
+
+ OS X 10.8 and earlier; see:
+ http://lists.gnu.org/archive/html/bug-gnulib/2012-12/msg00023.html
+
+ DragonFly; see
+ http://muscles.dragonflybsd.org/bulk/bleeding-edge-potential/latest-per-pkg/ah-tty-0.3.12.log
+
+ FreeBSD; see:
+ http://lists.gnu.org/archive/html/bug-gnulib/2014-07/msg00104.html
+
OS X 10.9 has a macro __header_inline indicating the bug is fixed for C and
for clang but remains for g++; see .
- Perhaps Apple will fix this some day. */
-#if (defined __APPLE__ \
+ Assume DragonFly and FreeBSD will be similar. */
+#if (((defined __APPLE__ && defined __MACH__) \
+ || defined __DragonFly__ || defined __FreeBSD__) \
&& (defined __header_inline \
? (defined __cplusplus && defined __GNUC_STDC_INLINE__ \
&& ! defined __clang__) \
@@ -33,19 +48,19 @@ AC_DEFUN([gl_EXTERN_INLINE],
&& (defined __GNUC__ || defined __cplusplus)) \
|| (defined _FORTIFY_SOURCE && 0 < _FORTIFY_SOURCE \
&& defined __GNUC__ && ! defined __cplusplus))))
-# define _GL_EXTERN_INLINE_APPLE_BUG
+# define _GL_EXTERN_INLINE_STDHEADER_BUG
#endif
#if ((__GNUC__ \
? defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ \
: (199901L <= __STDC_VERSION__ \
&& !defined __HP_cc \
&& !(defined __SUNPRO_C && __STDC__))) \
- && !defined _GL_EXTERN_INLINE_APPLE_BUG)
+ && !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
# define _GL_INLINE inline
# define _GL_EXTERN_INLINE extern inline
# define _GL_EXTERN_INLINE_IN_USE
#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ \
- && !defined _GL_EXTERN_INLINE_APPLE_BUG)
+ && !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
# if defined __GNUC_GNU_INLINE__ && __GNUC_GNU_INLINE__
/* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */
# define _GL_INLINE extern inline __attribute__ ((__gnu_inline__))
@@ -59,17 +74,19 @@ AC_DEFUN([gl_EXTERN_INLINE],
# define _GL_EXTERN_INLINE static _GL_UNUSED
#endif
-#if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
+/* In GCC 4.6 (inclusive) to 5.1 (exclusive),
+ suppress bogus "no previous prototype for 'FOO'"
+ and "no previous declaration for 'FOO'" diagnostics,
+ when FOO is an inline function in the header; see
+ and
+ . */
+#if __GNUC__ == 4 && 6 <= __GNUC_MINOR__
# if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__
# define _GL_INLINE_HEADER_CONST_PRAGMA
# else
# define _GL_INLINE_HEADER_CONST_PRAGMA \
_Pragma ("GCC diagnostic ignored \"-Wsuggest-attribute=const\"")
# endif
- /* Suppress GCC's bogus "no previous prototype for 'FOO'"
- and "no previous declaration for 'FOO'" diagnostics,
- when FOO is an inline function in the header; see
- . */
# define _GL_INLINE_HEADER_BEGIN \
_Pragma ("GCC diagnostic push") \
_Pragma ("GCC diagnostic ignored \"-Wmissing-prototypes\"") \
diff --git a/m4/fcntl-o.m4 b/m4/fcntl-o.m4
index 43c93124e..891a62fb6 100644
--- a/m4/fcntl-o.m4
+++ b/m4/fcntl-o.m4
@@ -1,5 +1,5 @@
# fcntl-o.m4 serial 4
-dnl Copyright (C) 2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/fcntl_h.m4 b/m4/fcntl_h.m4
index fb2556d37..b279162a1 100644
--- a/m4/fcntl_h.m4
+++ b/m4/fcntl_h.m4
@@ -1,6 +1,6 @@
# serial 15
# Configure fcntl.h.
-dnl Copyright (C) 2006-2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/flexmember.m4 b/m4/flexmember.m4
new file mode 100644
index 000000000..95500caf0
--- /dev/null
+++ b/m4/flexmember.m4
@@ -0,0 +1,41 @@
+# serial 3
+# Check for flexible array member support.
+
+# Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Written by Paul Eggert.
+
+AC_DEFUN([AC_C_FLEXIBLE_ARRAY_MEMBER],
+[
+ AC_CACHE_CHECK([for flexible array member],
+ ac_cv_c_flexmember,
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include
+ #include
+ #include
+ struct s { int n; double d[]; };]],
+ [[int m = getchar ();
+ struct s *p = malloc (offsetof (struct s, d)
+ + m * sizeof (double));
+ p->d[0] = 0.0;
+ return p->d != (double *) NULL;]])],
+ [ac_cv_c_flexmember=yes],
+ [ac_cv_c_flexmember=no])])
+ if test $ac_cv_c_flexmember = yes; then
+ AC_DEFINE([FLEXIBLE_ARRAY_MEMBER], [],
+ [Define to nothing if C supports flexible array members, and to
+ 1 if it does not. That way, with a declaration like 'struct s
+ { int n; double d@<:@FLEXIBLE_ARRAY_MEMBER@:>@; };', the struct hack
+ can be used with pre-C99 compilers.
+ When computing the size of such an object, don't use 'sizeof (struct s)'
+ as it overestimates the size. Use 'offsetof (struct s, d)' instead.
+ Don't use 'offsetof (struct s, d@<:@0@:>@)', as this doesn't work with
+ MSVC and with C++ compilers.])
+ else
+ AC_DEFINE([FLEXIBLE_ARRAY_MEMBER], [1])
+ fi
+])
diff --git a/m4/float_h.m4 b/m4/float_h.m4
index a27ef7f97..e4853f3b0 100644
--- a/m4/float_h.m4
+++ b/m4/float_h.m4
@@ -1,5 +1,5 @@
# float_h.m4 serial 9
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/flock.m4 b/m4/flock.m4
index ad2d1290c..ee2941a33 100644
--- a/m4/flock.m4
+++ b/m4/flock.m4
@@ -1,5 +1,5 @@
# flock.m4 serial 3
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/floor.m4 b/m4/floor.m4
index a38c03d14..41195d9d2 100644
--- a/m4/floor.m4
+++ b/m4/floor.m4
@@ -1,5 +1,5 @@
# floor.m4 serial 8
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/fpieee.m4 b/m4/fpieee.m4
index 729afe859..41c6033b3 100644
--- a/m4/fpieee.m4
+++ b/m4/fpieee.m4
@@ -1,5 +1,5 @@
-# fpieee.m4 serial 2
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+# fpieee.m4 serial 2 -*- coding: utf-8 -*-
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/frexp.m4 b/m4/frexp.m4
index 579826213..04f40679a 100644
--- a/m4/frexp.m4
+++ b/m4/frexp.m4
@@ -1,5 +1,5 @@
# frexp.m4 serial 15
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/fstat.m4 b/m4/fstat.m4
index ddd3fb976..d6a928827 100644
--- a/m4/fstat.m4
+++ b/m4/fstat.m4
@@ -1,5 +1,5 @@
# fstat.m4 serial 4
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/fsync.m4 b/m4/fsync.m4
index 888a65def..bc2b09352 100644
--- a/m4/fsync.m4
+++ b/m4/fsync.m4
@@ -1,5 +1,5 @@
# fsync.m4 serial 2
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/func.m4 b/m4/func.m4
index 0ab14c9e4..fcc74ff2c 100644
--- a/m4/func.m4
+++ b/m4/func.m4
@@ -1,5 +1,5 @@
# func.m4 serial 2
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/getaddrinfo.m4 b/m4/getaddrinfo.m4
index 2e6658486..f3e40c479 100644
--- a/m4/getaddrinfo.m4
+++ b/m4/getaddrinfo.m4
@@ -1,5 +1,5 @@
# getaddrinfo.m4 serial 30
-dnl Copyright (C) 2004-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2004-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/getlogin.m4 b/m4/getlogin.m4
index 47b8f0897..0db7d51be 100644
--- a/m4/getlogin.m4
+++ b/m4/getlogin.m4
@@ -1,5 +1,5 @@
# getlogin.m4 serial 3
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/gettimeofday.m4 b/m4/gettimeofday.m4
index 1c2d66ee2..ce246e18b 100644
--- a/m4/gettimeofday.m4
+++ b/m4/gettimeofday.m4
@@ -1,6 +1,6 @@
# serial 21
-# Copyright (C) 2001-2003, 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001-2003, 2005, 2007, 2009-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/glibc21.m4 b/m4/glibc21.m4
index ab58b7121..3a971c5a2 100644
--- a/m4/glibc21.m4
+++ b/m4/glibc21.m4
@@ -1,5 +1,5 @@
# glibc21.m4 serial 5
-dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2014 Free Software Foundation,
+dnl Copyright (C) 2000-2002, 2004, 2008, 2010-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4
index 26c96b3e3..57d23662c 100644
--- a/m4/gnulib-cache.m4
+++ b/m4/gnulib-cache.m4
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2015 Free Software Foundation, Inc.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -76,7 +76,6 @@ gl_MODULES([
isfinite
isinf
isnan
- largefile
ldexp
lib-symbol-versions
lib-symbol-visibility
@@ -127,7 +126,7 @@ gl_MODULES([
warnings
wchar
])
-gl_AVOID([lock])
+gl_AVOID([ lock])
gl_SOURCE_BASE([lib])
gl_M4_BASE([m4])
gl_PO_BASE([])
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 20ce40e74..50ef97420 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,5 +1,5 @@
-# gnulib-common.m4 serial 34
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# gnulib-common.m4 serial 36
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -49,6 +49,16 @@ AC_DEFUN([gl_COMMON_BODY], [
is a misnomer outside of parameter lists. */
#define _UNUSED_PARAMETER_ _GL_UNUSED
+/* gcc supports the "unused" attribute on possibly unused labels, and
+ g++ has since version 4.5. Note to support C++ as well as C,
+ _GL_UNUSED_LABEL should be used with a trailing ; */
+#if !defined __cplusplus || __GNUC__ > 4 \
+ || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# define _GL_UNUSED_LABEL _GL_UNUSED
+#else
+# define _GL_UNUSED_LABEL
+#endif
+
/* The __pure__ attribute was added in gcc 2.96. */
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
@@ -243,9 +253,10 @@ AC_DEFUN([gl_PROG_AR_RANLIB],
[
dnl Minix 3 comes with two toolchains: The Amsterdam Compiler Kit compiler
dnl as "cc", and GCC as "gcc". They have different object file formats and
- dnl library formats. In particular, the GNU binutils programs ar, ranlib
+ dnl library formats. In particular, the GNU binutils programs ar and ranlib
dnl produce libraries that work only with gcc, not with cc.
AC_REQUIRE([AC_PROG_CC])
+ AC_BEFORE([$0], [AM_PROG_AR])
AC_CACHE_CHECK([for Minix Amsterdam compiler], [gl_cv_c_amsterdam_compiler],
[
AC_EGREP_CPP([Amsterdam],
@@ -257,25 +268,37 @@ Amsterdam
[gl_cv_c_amsterdam_compiler=yes],
[gl_cv_c_amsterdam_compiler=no])
])
- if test -z "$AR"; then
- if test $gl_cv_c_amsterdam_compiler = yes; then
+
+ dnl Don't compete with AM_PROG_AR's decision about AR/ARFLAGS if we are not
+ dnl building with __ACK__.
+ if test $gl_cv_c_amsterdam_compiler = yes; then
+ if test -z "$AR"; then
AR='cc -c.a'
- if test -z "$ARFLAGS"; then
- ARFLAGS='-o'
- fi
- else
- dnl Use the Automake-documented default values for AR and ARFLAGS,
- dnl but prefer ${host}-ar over ar (useful for cross-compiling).
- AC_CHECK_TOOL([AR], [ar], [ar])
- if test -z "$ARFLAGS"; then
- ARFLAGS='cru'
- fi
+ fi
+ if test -z "$ARFLAGS"; then
+ ARFLAGS='-o'
fi
else
- if test -z "$ARFLAGS"; then
- ARFLAGS='cru'
- fi
+ dnl AM_PROG_AR was added in automake v1.11.2. AM_PROG_AR does not AC_SUBST
+ dnl ARFLAGS variable (it is filed into Makefile.in directly by automake
+ dnl script on-demand, if not specified by ./configure of course).
+ dnl Don't AC_REQUIRE the AM_PROG_AR otherwise the code for __ACK__ above
+ dnl will be ignored. Also, pay attention to call AM_PROG_AR in else block
+ dnl because AM_PROG_AR is written so it could re-set AR variable even for
+ dnl __ACK__. It may seem like its easier to avoid calling the macro here,
+ dnl but we need to AC_SUBST both AR/ARFLAGS (thus those must have some good
+ dnl default value and automake should usually know them).
+ m4_ifdef([AM_PROG_AR], [AM_PROG_AR], [:])
fi
+
+ dnl In case the code above has not helped with setting AR/ARFLAGS, use
+ dnl Automake-documented default values for AR and ARFLAGS, but prefer
+ dnl ${host}-ar over ar (useful for cross-compiling).
+ AC_CHECK_TOOL([AR], [ar], [ar])
+ if test -z "$ARFLAGS"; then
+ ARFLAGS='cr'
+ fi
+
AC_SUBST([AR])
AC_SUBST([ARFLAGS])
if test -z "$RANLIB"; then
@@ -309,26 +332,28 @@ m4_ifdef([AC_PROG_MKDIR_P], [
])
# AC_C_RESTRICT
-# This definition overrides the AC_C_RESTRICT macro from autoconf 2.60..2.61,
-# so that mixed use of GNU C and GNU C++ and mixed use of Sun C and Sun C++
-# works.
-# This definition can be removed once autoconf >= 2.62 can be assumed.
-# AC_AUTOCONF_VERSION was introduced in 2.62, so use that as the witness.
-m4_ifndef([AC_AUTOCONF_VERSION],[
+# This definition is copied from post-2.69 Autoconf and overrides the
+# AC_C_RESTRICT macro from autoconf 2.60..2.69. It can be removed
+# once autoconf >= 2.70 can be assumed. It's painful to check version
+# numbers, and in practice this macro is more up-to-date than Autoconf
+# is, so override Autoconf unconditionally.
AC_DEFUN([AC_C_RESTRICT],
[AC_CACHE_CHECK([for C/C++ restrict keyword], [ac_cv_c_restrict],
[ac_cv_c_restrict=no
# The order here caters to the fact that C++ does not require restrict.
for ac_kw in __restrict __restrict__ _Restrict restrict; do
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
- [[typedef int * int_ptr;
- int foo (int_ptr $ac_kw ip) {
- return ip[0];
- }]],
- [[int s[1];
- int * $ac_kw t = s;
- t[0] = 0;
- return foo(t)]])],
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[typedef int *int_ptr;
+ int foo (int_ptr $ac_kw ip) { return ip[0]; }
+ int bar (int [$ac_kw]); /* Catch GCC bug 14050. */
+ int bar (int ip[$ac_kw]) { return ip[0]; }
+ ]],
+ [[int s[1];
+ int *$ac_kw t = s;
+ t[0] = 0;
+ return foo (t) + bar (t);
+ ]])],
[ac_cv_c_restrict=$ac_kw])
test "$ac_cv_c_restrict" != no && break
done
@@ -338,21 +363,21 @@ AC_DEFUN([AC_C_RESTRICT],
nothing if this is not supported. Do not define if restrict is
supported directly. */
#undef restrict
-/* Work around a bug in Sun C++: it does not support _Restrict, even
- though the corresponding Sun C compiler does, which causes
- "#define restrict _Restrict" in the previous line. Perhaps some future
- version of Sun C++ will work with _Restrict; if so, it'll probably
- define __RESTRICT, just as Sun C does. */
+/* Work around a bug in Sun C++: it does not support _Restrict or
+ __restrict__, even though the corresponding Sun C compiler ends up with
+ "#define restrict _Restrict" or "#define restrict __restrict__" in the
+ previous line. Perhaps some future version of Sun C++ will work with
+ restrict; if so, hopefully it defines __RESTRICT like Sun C does. */
#if defined __SUNPRO_CC && !defined __RESTRICT
# define _Restrict
+# define __restrict__
#endif])
case $ac_cv_c_restrict in
restrict) ;;
no) AC_DEFINE([restrict], []) ;;
*) AC_DEFINE_UNQUOTED([restrict], [$ac_cv_c_restrict]) ;;
esac
-])
-])
+])# AC_C_RESTRICT
# gl_BIGENDIAN
# is like AC_C_BIGENDIAN, except that it can be AC_REQUIREd.
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 429fee422..a52f18593 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -1,5 +1,5 @@
# DO NOT EDIT! GENERATED AUTOMATICALLY!
-# Copyright (C) 2002-2014 Free Software Foundation, Inc.
+# Copyright (C) 2002-2015 Free Software Foundation, Inc.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,7 +37,11 @@ AC_DEFUN([gl_EARLY],
m4_pattern_allow([^gl_ES$])dnl a valid locale name
m4_pattern_allow([^gl_LIBOBJS$])dnl a variable
m4_pattern_allow([^gl_LTLIBOBJS$])dnl a variable
+
+ # Pre-early section.
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
AC_REQUIRE([gl_PROG_AR_RANLIB])
+
AC_REQUIRE([AM_PROG_CC_C_O])
# Code from module absolute-header:
# Code from module accept:
@@ -46,6 +50,7 @@ AC_DEFUN([gl_EARLY],
# Code from module alloca-opt:
# Code from module announce-gen:
# Code from module arpa_inet:
+ # Code from module assure:
# Code from module autobuild:
AB_INIT
# Code from module binary-io:
@@ -73,10 +78,10 @@ AC_DEFUN([gl_EARLY],
# Code from module environ:
# Code from module errno:
# Code from module extensions:
- AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
# Code from module extern-inline:
# Code from module fcntl-h:
# Code from module fd-hook:
+ # Code from module flexmember:
# Code from module float:
# Code from module flock:
# Code from module floor:
@@ -147,6 +152,8 @@ AC_DEFUN([gl_EARLY],
# Code from module memchr:
# Code from module mkdir:
# Code from module mkstemp:
+ # Code from module mktime:
+ # Code from module mktime-internal:
# Code from module msvc-inval:
# Code from module msvc-nothrow:
# Code from module multiarch:
@@ -220,6 +227,8 @@ AC_DEFUN([gl_EARLY],
# Code from module tempname:
# Code from module time:
# Code from module time_r:
+ # Code from module time_rz:
+ # Code from module timegm:
# Code from module times:
# Code from module trunc:
# Code from module unistd:
@@ -230,6 +239,7 @@ AC_DEFUN([gl_EARLY],
# Code from module unistr/u8-prev:
# Code from module unistr/u8-uctomb:
# Code from module unitypes:
+ # Code from module unsetenv:
# Code from module useless-if-before-free:
# Code from module vasnprintf:
# Code from module vc-list-files:
@@ -337,6 +347,7 @@ AC_SUBST([LTALLOCA])
gl_HEADER_ERRNO_H
AC_REQUIRE([gl_EXTERN_INLINE])
gl_FCNTL_H
+ AC_C_FLEXIBLE_ARRAY_MEMBER
gl_FLOAT_H
if test $REPLACE_FLOAT_LDBL = 1; then
AC_LIBOBJ([float])
@@ -591,6 +602,17 @@ AC_SUBST([LTALLOCA])
gl_PREREQ_MKSTEMP
fi
gl_STDLIB_MODULE_INDICATOR([mkstemp])
+ gl_FUNC_MKTIME
+ if test $REPLACE_MKTIME = 1; then
+ AC_LIBOBJ([mktime])
+ gl_PREREQ_MKTIME
+ fi
+ gl_TIME_MODULE_INDICATOR([mktime])
+ gl_FUNC_MKTIME_INTERNAL
+ if test $REPLACE_MKTIME = 1; then
+ AC_LIBOBJ([mktime])
+ gl_PREREQ_MKTIME
+ fi
gl_MSVC_INVAL
if test $HAVE_MSVC_INVALID_PARAMETER_HANDLER = 1; then
AC_LIBOBJ([msvc-inval])
@@ -749,8 +771,8 @@ AC_SUBST([LTALLOCA])
SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS=1
fi
gl_SYS_SOCKET_MODULE_INDICATOR([socket])
- gl_SOCKETLIB
- gl_SOCKETS
+ AC_REQUIRE([gl_SOCKETLIB])
+ AC_REQUIRE([gl_SOCKETS])
gl_TYPE_SOCKLEN_T
gt_TYPE_SSIZE_T
gl_FUNC_STAT
@@ -783,7 +805,7 @@ AC_SUBST([LTALLOCA])
AC_PROG_MKDIR_P
gl_HEADER_SYS_SELECT
AC_PROG_MKDIR_P
- gl_HEADER_SYS_SOCKET
+ AC_REQUIRE([gl_HEADER_SYS_SOCKET])
AC_PROG_MKDIR_P
gl_HEADER_SYS_STAT_H
AC_PROG_MKDIR_P
@@ -803,6 +825,17 @@ AC_SUBST([LTALLOCA])
gl_PREREQ_TIME_R
fi
gl_TIME_MODULE_INDICATOR([time_r])
+ gl_TIME_RZ
+ if test "$HAVE_TIMEZONE_T" = 0; then
+ AC_LIBOBJ([time_rz])
+ fi
+ gl_TIME_MODULE_INDICATOR([time_rz])
+ gl_FUNC_TIMEGM
+ if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then
+ AC_LIBOBJ([timegm])
+ gl_PREREQ_TIMEGM
+ fi
+ gl_TIME_MODULE_INDICATOR([timegm])
gl_FUNC_TIMES
if test $HAVE_TIMES = 0; then
AC_LIBOBJ([times])
@@ -814,7 +847,7 @@ AC_SUBST([LTALLOCA])
fi
gl_MATH_MODULE_INDICATOR([trunc])
gl_UNISTD_H
- gl_LIBUNISTRING_LIBHEADER([0.9.2], [unistr.h])
+ gl_LIBUNISTRING_LIBHEADER([0.9.4], [unistr.h])
gl_MODULE_INDICATOR([unistr/u8-mbtouc])
gl_LIBUNISTRING_MODULE([0.9.4], [unistr/u8-mbtouc])
gl_MODULE_INDICATOR([unistr/u8-mbtouc-unsafe])
@@ -824,7 +857,13 @@ AC_SUBST([LTALLOCA])
gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-prev])
gl_MODULE_INDICATOR([unistr/u8-uctomb])
gl_LIBUNISTRING_MODULE([0.9], [unistr/u8-uctomb])
- gl_LIBUNISTRING_LIBHEADER([0.9], [unitypes.h])
+ gl_LIBUNISTRING_LIBHEADER([0.9.4], [unitypes.h])
+ gl_FUNC_UNSETENV
+ if test $HAVE_UNSETENV = 0 || test $REPLACE_UNSETENV = 1; then
+ AC_LIBOBJ([unsetenv])
+ gl_PREREQ_UNSETENV
+ fi
+ gl_STDLIB_MODULE_INDICATOR([unsetenv])
gl_FUNC_VASNPRINTF
gl_FUNC_VSNPRINTF
gl_STDIO_MODULE_INDICATOR([vsnprintf])
@@ -994,12 +1033,14 @@ AC_DEFUN([gl_FILE_LIST], [
build-aux/useless-if-before-free
build-aux/vc-list-files
doc/gendocs_template
+ doc/gendocs_template_min
lib/accept.c
lib/alignof.h
lib/alloca.c
lib/alloca.in.h
lib/arpa_inet.in.h
lib/asnprintf.c
+ lib/assure.h
lib/basename-lgpl.c
lib/binary-io.c
lib/binary-io.h
@@ -1096,6 +1137,8 @@ AC_DEFUN([gl_FILE_LIST], [
lib/memchr.valgrind
lib/mkdir.c
lib/mkstemp.c
+ lib/mktime-internal.h
+ lib/mktime.c
lib/msvc-inval.c
lib/msvc-inval.h
lib/msvc-nothrow.c
@@ -1181,8 +1224,11 @@ AC_DEFUN([gl_FILE_LIST], [
lib/sys_uio.in.h
lib/tempname.c
lib/tempname.h
+ lib/time-internal.h
lib/time.in.h
lib/time_r.c
+ lib/time_rz.c
+ lib/timegm.c
lib/times.c
lib/trunc.c
lib/unistd.c
@@ -1197,6 +1243,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/unistr/u8-uctomb-aux.c
lib/unistr/u8-uctomb.c
lib/unitypes.in.h
+ lib/unsetenv.c
lib/vasnprintf.c
lib/vasnprintf.h
lib/verify.h
@@ -1240,6 +1287,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/extern-inline.m4
m4/fcntl-o.m4
m4/fcntl_h.m4
+ m4/flexmember.m4
m4/float_h.m4
m4/flock.m4
m4/floor.m4
@@ -1301,6 +1349,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/memchr.m4
m4/mkdir.m4
m4/mkstemp.m4
+ m4/mktime.m4
m4/mmap-anon.m4
m4/mode_t.m4
m4/msvc-inval.m4
@@ -1365,6 +1414,8 @@ AC_DEFUN([gl_FILE_LIST], [
m4/tempname.m4
m4/time_h.m4
m4/time_r.m4
+ m4/time_rz.m4
+ m4/timegm.m4
m4/times.m4
m4/tm_gmtoff.m4
m4/trunc.m4
diff --git a/m4/gnulib-tool.m4 b/m4/gnulib-tool.m4
index a588e1519..fede1fc3b 100644
--- a/m4/gnulib-tool.m4
+++ b/m4/gnulib-tool.m4
@@ -1,5 +1,5 @@
# gnulib-tool.m4 serial 2
-dnl Copyright (C) 2004-2005, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2004-2005, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/hostent.m4 b/m4/hostent.m4
index dd8fc0709..6706d1f5c 100644
--- a/m4/hostent.m4
+++ b/m4/hostent.m4
@@ -1,5 +1,5 @@
# hostent.m4 serial 2
-dnl Copyright (C) 2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/iconv.m4 b/m4/iconv.m4
index 4b29c5f2c..4e3736315 100644
--- a/m4/iconv.m4
+++ b/m4/iconv.m4
@@ -1,4 +1,4 @@
-# iconv.m4 serial 18 (gettext-0.18.2)
+# iconv.m4 serial 19 (gettext-0.18.2)
dnl Copyright (C) 2000-2002, 2007-2014 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -72,27 +72,33 @@ AC_DEFUN([AM_ICONV_LINK],
if test $am_cv_lib_iconv = yes; then
LIBS="$LIBS $LIBICONV"
fi
- AC_RUN_IFELSE(
- [AC_LANG_SOURCE([[
+ am_cv_func_iconv_works=no
+ for ac_iconv_const in '' 'const'; do
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
#include
#include
-int main ()
-{
- int result = 0;
+
+#ifndef ICONV_CONST
+# define ICONV_CONST $ac_iconv_const
+#endif
+ ]],
+ [[int result = 0;
/* Test against AIX 5.1 bug: Failures are not distinguishable from successful
returns. */
{
iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8");
if (cd_utf8_to_88591 != (iconv_t)(-1))
{
- static const char input[] = "\342\202\254"; /* EURO SIGN */
+ static ICONV_CONST char input[] = "\342\202\254"; /* EURO SIGN */
char buf[10];
- const char *inptr = input;
+ ICONV_CONST char *inptr = input;
size_t inbytesleft = strlen (input);
char *outptr = buf;
size_t outbytesleft = sizeof (buf);
size_t res = iconv (cd_utf8_to_88591,
- (char **) &inptr, &inbytesleft,
+ &inptr, &inbytesleft,
&outptr, &outbytesleft);
if (res == 0)
result |= 1;
@@ -105,14 +111,14 @@ int main ()
iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646");
if (cd_ascii_to_88591 != (iconv_t)(-1))
{
- static const char input[] = "\263";
+ static ICONV_CONST char input[] = "\263";
char buf[10];
- const char *inptr = input;
+ ICONV_CONST char *inptr = input;
size_t inbytesleft = strlen (input);
char *outptr = buf;
size_t outbytesleft = sizeof (buf);
size_t res = iconv (cd_ascii_to_88591,
- (char **) &inptr, &inbytesleft,
+ &inptr, &inbytesleft,
&outptr, &outbytesleft);
if (res == 0)
result |= 2;
@@ -124,14 +130,14 @@ int main ()
iconv_t cd_88591_to_utf8 = iconv_open ("UTF-8", "ISO-8859-1");
if (cd_88591_to_utf8 != (iconv_t)(-1))
{
- static const char input[] = "\304";
+ static ICONV_CONST char input[] = "\304";
static char buf[2] = { (char)0xDE, (char)0xAD };
- const char *inptr = input;
+ ICONV_CONST char *inptr = input;
size_t inbytesleft = 1;
char *outptr = buf;
size_t outbytesleft = 1;
size_t res = iconv (cd_88591_to_utf8,
- (char **) &inptr, &inbytesleft,
+ &inptr, &inbytesleft,
&outptr, &outbytesleft);
if (res != (size_t)(-1) || outptr - buf > 1 || buf[1] != (char)0xAD)
result |= 4;
@@ -144,14 +150,14 @@ int main ()
iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591");
if (cd_88591_to_utf8 != (iconv_t)(-1))
{
- static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
+ static ICONV_CONST char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337";
char buf[50];
- const char *inptr = input;
+ ICONV_CONST char *inptr = input;
size_t inbytesleft = strlen (input);
char *outptr = buf;
size_t outbytesleft = sizeof (buf);
size_t res = iconv (cd_88591_to_utf8,
- (char **) &inptr, &inbytesleft,
+ &inptr, &inbytesleft,
&outptr, &outbytesleft);
if ((int)res > 0)
result |= 8;
@@ -171,17 +177,14 @@ int main ()
&& iconv_open ("utf8", "eucJP") == (iconv_t)(-1))
result |= 16;
return result;
-}]])],
- [am_cv_func_iconv_works=yes],
- [am_cv_func_iconv_works=no],
- [
-changequote(,)dnl
- case "$host_os" in
- aix* | hpux*) am_cv_func_iconv_works="guessing no" ;;
- *) am_cv_func_iconv_works="guessing yes" ;;
- esac
-changequote([,])dnl
- ])
+]])],
+ [am_cv_func_iconv_works=yes], ,
+ [case "$host_os" in
+ aix* | hpux*) am_cv_func_iconv_works="guessing no" ;;
+ *) am_cv_func_iconv_works="guessing yes" ;;
+ esac])
+ test "$am_cv_func_iconv_works" = no || break
+ done
LIBS="$am_save_LIBS"
])
case "$am_cv_func_iconv_works" in
diff --git a/m4/iconv_h.m4 b/m4/iconv_h.m4
index e992fa399..c6878faa0 100644
--- a/m4/iconv_h.m4
+++ b/m4/iconv_h.m4
@@ -1,5 +1,5 @@
# iconv_h.m4 serial 8
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/iconv_open-utf.m4 b/m4/iconv_open-utf.m4
index 31ced265a..bd81e66d8 100644
--- a/m4/iconv_open-utf.m4
+++ b/m4/iconv_open-utf.m4
@@ -1,5 +1,5 @@
# iconv_open-utf.m4 serial 1
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/iconv_open.m4 b/m4/iconv_open.m4
index e0bfd7203..177fccba1 100644
--- a/m4/iconv_open.m4
+++ b/m4/iconv_open.m4
@@ -1,5 +1,5 @@
# iconv_open.m4 serial 14
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/include_next.m4 b/m4/include_next.m4
index 69ad3dbb0..233d254e8 100644
--- a/m4/include_next.m4
+++ b/m4/include_next.m4
@@ -1,5 +1,5 @@
# include_next.m4 serial 23
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/inet_ntop.m4 b/m4/inet_ntop.m4
index 5b27759c5..df2bc746d 100644
--- a/m4/inet_ntop.m4
+++ b/m4/inet_ntop.m4
@@ -1,5 +1,5 @@
# inet_ntop.m4 serial 19
-dnl Copyright (C) 2005-2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005-2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/inet_pton.m4 b/m4/inet_pton.m4
index 136ed24d0..433faee0c 100644
--- a/m4/inet_pton.m4
+++ b/m4/inet_pton.m4
@@ -1,5 +1,5 @@
# inet_pton.m4 serial 17
-dnl Copyright (C) 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/inline.m4 b/m4/inline.m4
index c49957f80..dc7063e7e 100644
--- a/m4/inline.m4
+++ b/m4/inline.m4
@@ -1,5 +1,5 @@
# inline.m4 serial 4
-dnl Copyright (C) 2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/intmax_t.m4 b/m4/intmax_t.m4
index af5561e5d..4bd8155c8 100644
--- a/m4/intmax_t.m4
+++ b/m4/intmax_t.m4
@@ -1,5 +1,5 @@
# intmax_t.m4 serial 8
-dnl Copyright (C) 1997-2004, 2006-2007, 2009-2014 Free Software Foundation,
+dnl Copyright (C) 1997-2004, 2006-2007, 2009-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4
index 87be9cfb5..d0b5f5d98 100644
--- a/m4/inttypes_h.m4
+++ b/m4/inttypes_h.m4
@@ -1,5 +1,5 @@
# inttypes_h.m4 serial 10
-dnl Copyright (C) 1997-2004, 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 1997-2004, 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/isfinite.m4 b/m4/isfinite.m4
index 53ad9092a..00d7e8042 100644
--- a/m4/isfinite.m4
+++ b/m4/isfinite.m4
@@ -1,5 +1,5 @@
-# isfinite.m4 serial 13
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# isfinite.m4 serial 15
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -34,13 +34,8 @@ AC_DEFUN([gl_ISFINITE],
AC_SUBST([ISFINITE_LIBM])
])
-dnl Test whether isfinite() on 'long double' recognizes all numbers which are
-dnl neither finite nor infinite. This test fails e.g. on i686, x86_64, ia64,
-dnl because of
-dnl - pseudo-denormals on x86_64,
-dnl - pseudo-zeroes, unnormalized numbers, and pseudo-denormals on i686,
-dnl - pseudo-NaN, pseudo-Infinity, pseudo-zeroes, unnormalized numbers, and
-dnl pseudo-denormals on ia64.
+dnl Test whether isfinite() on 'long double' recognizes all canonical values
+dnl which are neither finite nor infinite.
AC_DEFUN([gl_ISFINITEL_WORKS],
[
AC_REQUIRE([AC_PROG_CC])
@@ -94,7 +89,7 @@ int main ()
# ifdef WORDS_BIGENDIAN
# define LDBL80_WORDS(exponent,manthi,mantlo) \
{ ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
- ((unsigned int) (manthi) << 16) | (unsigned int) (mantlo) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
(unsigned int) (mantlo) << 16 \
}
# else
@@ -114,52 +109,41 @@ int main ()
if (isfinite (x.value))
result |= 2;
}
- /* The isfinite macro should recognize Pseudo-NaNs, Pseudo-Infinities,
- Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals, as defined in
- Intel IA-64 Architecture Software Developer's Manual, Volume 1:
- Application Architecture.
- Table 5-2 "Floating-Point Register Encodings"
- Figure 5-6 "Memory to Floating-Point Register Data Translation"
- */
+ /* isfinite should return something even for noncanonical values. */
{ /* Pseudo-NaN. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
- if (isfinite (x.value))
+ if (isfinite (x.value) && !isfinite (x.value))
result |= 4;
}
{ /* Pseudo-Infinity. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
- if (isfinite (x.value))
+ if (isfinite (x.value) && !isfinite (x.value))
result |= 8;
}
{ /* Pseudo-Zero. */
static memory_long_double x =
{ LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
- if (isfinite (x.value))
+ if (isfinite (x.value) && !isfinite (x.value))
result |= 16;
}
{ /* Unnormalized number. */
static memory_long_double x =
{ LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
- if (isfinite (x.value))
+ if (isfinite (x.value) && !isfinite (x.value))
result |= 32;
}
{ /* Pseudo-Denormal. */
static memory_long_double x =
{ LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
- if (isfinite (x.value))
+ if (isfinite (x.value) && !isfinite (x.value))
result |= 64;
}
#endif
return result;
}]])], [gl_cv_func_isfinitel_works=yes], [gl_cv_func_isfinitel_works=no],
- [case "$host_cpu" in
- # Guess no on ia64, x86_64, i386.
- ia64 | x86_64 | i*86) gl_cv_func_isfinitel_works="guessing no";;
- *) gl_cv_func_isfinitel_works="guessing yes";;
- esac
- ])
+ [gl_cv_func_isfinitel_works="guessing yes"])
])
])
diff --git a/m4/isinf.m4 b/m4/isinf.m4
index 7174acecd..b0a3da330 100644
--- a/m4/isinf.m4
+++ b/m4/isinf.m4
@@ -1,5 +1,5 @@
-# isinf.m4 serial 9
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# isinf.m4 serial 11
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -37,13 +37,8 @@ AC_DEFUN([gl_ISINF],
dnl Test whether isinf() works:
dnl 1) Whether it correctly returns false for LDBL_MAX.
-dnl 2) Whether on 'long double' recognizes all numbers which are neither
-dnl finite nor infinite. This test fails on OpenBSD/x86, but could also
-dnl fail e.g. on i686, x86_64, ia64, because of
-dnl - pseudo-denormals on x86_64,
-dnl - pseudo-zeroes, unnormalized numbers, and pseudo-denormals on i686,
-dnl - pseudo-NaN, pseudo-Infinity, pseudo-zeroes, unnormalized numbers, and
-dnl pseudo-denormals on ia64.
+dnl 2) Whether on 'long double' recognizes all canonical values which are
+dnl infinite.
AC_DEFUN([gl_ISINFL_WORKS],
[
AC_REQUIRE([AC_PROG_CC])
@@ -101,7 +96,7 @@ int main ()
# ifdef WORDS_BIGENDIAN
# define LDBL80_WORDS(exponent,manthi,mantlo) \
{ ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
- ((unsigned int) (manthi) << 16) | (unsigned int) (mantlo) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
(unsigned int) (mantlo) << 16 \
}
# else
@@ -121,55 +116,41 @@ int main ()
if (isinf (x.value))
result |= 2;
}
- /* The isinf macro should recognize Pseudo-NaNs, Pseudo-Infinities,
- Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals, as defined in
- Intel IA-64 Architecture Software Developer's Manual, Volume 1:
- Application Architecture.
- Table 5-2 "Floating-Point Register Encodings"
- Figure 5-6 "Memory to Floating-Point Register Data Translation"
- */
+ /* isinf should return something even for noncanonical values. */
{ /* Pseudo-NaN. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
- if (isinf (x.value))
+ if (isinf (x.value) && !isinf (x.value))
result |= 4;
}
{ /* Pseudo-Infinity. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
- if (isinf (x.value))
+ if (isinf (x.value) && !isinf (x.value))
result |= 8;
}
{ /* Pseudo-Zero. */
static memory_long_double x =
{ LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
- if (isinf (x.value))
+ if (isinf (x.value) && !isinf (x.value))
result |= 16;
}
{ /* Unnormalized number. */
static memory_long_double x =
{ LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
- if (isinf (x.value))
+ if (isinf (x.value) && !isinf (x.value))
result |= 32;
}
{ /* Pseudo-Denormal. */
static memory_long_double x =
{ LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
- if (isinf (x.value))
+ if (isinf (x.value) && !isinf (x.value))
result |= 64;
}
#endif
return result;
}]])], [gl_cv_func_isinfl_works=yes], [gl_cv_func_isinfl_works=no],
- [
- case "$host" in
- # Guess no on OpenBSD ia64, x86_64, i386.
- ia64-*-openbsd* | x86_64-*-openbsd* | i*86-*-openbsd*)
- gl_cv_func_isinfl_works="guessing no";;
- *)
- gl_cv_func_isinfl_works="guessing yes";;
- esac
- ])
+ [gl_cv_func_isinfl_works="guessing yes"])
])
])
diff --git a/m4/isnan.m4 b/m4/isnan.m4
index 579340312..618e56e67 100644
--- a/m4/isnan.m4
+++ b/m4/isnan.m4
@@ -1,5 +1,5 @@
# isnan.m4 serial 5
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/isnand.m4 b/m4/isnand.m4
index 36e4ea307..4d5c615bc 100644
--- a/m4/isnand.m4
+++ b/m4/isnand.m4
@@ -1,5 +1,5 @@
# isnand.m4 serial 11
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/isnanf.m4 b/m4/isnanf.m4
index 1f2717d5e..09c3e5edd 100644
--- a/m4/isnanf.m4
+++ b/m4/isnanf.m4
@@ -1,5 +1,5 @@
# isnanf.m4 serial 14
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/isnanl.m4 b/m4/isnanl.m4
index 98b2b69fc..b86ca9efe 100644
--- a/m4/isnanl.m4
+++ b/m4/isnanl.m4
@@ -1,5 +1,5 @@
-# isnanl.m4 serial 17
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# isnanl.m4 serial 19
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -108,11 +108,8 @@ AC_DEFUN([gl_HAVE_ISNANL_IN_LIBM],
])
])
-dnl Test whether isnanl() recognizes all numbers which are neither finite nor
-dnl infinite. This test fails e.g. on NetBSD/i386 and on glibc/ia64.
-dnl Also, the GCC >= 4.0 built-in __builtin_isnanl does not pass the tests
-dnl - for pseudo-denormals on i686 and x86_64,
-dnl - for pseudo-zeroes, unnormalized numbers, and pseudo-denormals on ia64.
+dnl Test whether isnanl() recognizes all canonical numbers which are neither
+dnl finite nor infinite.
AC_DEFUN([gl_FUNC_ISNANL_WORKS],
[
AC_REQUIRE([AC_PROG_CC])
@@ -177,7 +174,7 @@ int main ()
# ifdef WORDS_BIGENDIAN
# define LDBL80_WORDS(exponent,manthi,mantlo) \
{ ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
- ((unsigned int) (manthi) << 16) | (unsigned int) (mantlo) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
(unsigned int) (mantlo) << 16 \
}
# else
@@ -197,41 +194,35 @@ int main ()
if (!isnanl (x.value))
result |= 2;
}
- /* The isnanl function should recognize Pseudo-NaNs, Pseudo-Infinities,
- Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals, as defined in
- Intel IA-64 Architecture Software Developer's Manual, Volume 1:
- Application Architecture.
- Table 5-2 "Floating-Point Register Encodings"
- Figure 5-6 "Memory to Floating-Point Register Data Translation"
- */
+ /* isnanl should return something even for noncanonical values. */
{ /* Pseudo-NaN. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
- if (!isnanl (x.value))
+ if (isnanl (x.value) && !isnanl (x.value))
result |= 4;
}
{ /* Pseudo-Infinity. */
static memory_long_double x =
{ LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
- if (!isnanl (x.value))
+ if (isnanl (x.value) && !isnanl (x.value))
result |= 8;
}
{ /* Pseudo-Zero. */
static memory_long_double x =
{ LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
- if (!isnanl (x.value))
+ if (isnanl (x.value) && !isnanl (x.value))
result |= 16;
}
{ /* Unnormalized number. */
static memory_long_double x =
{ LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
- if (!isnanl (x.value))
+ if (isnanl (x.value) && !isnanl (x.value))
result |= 32;
}
{ /* Pseudo-Denormal. */
static memory_long_double x =
{ LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
- if (!isnanl (x.value))
+ if (isnanl (x.value) && !isnanl (x.value))
result |= 64;
}
#endif
@@ -240,16 +231,6 @@ int main ()
}]])],
[gl_cv_func_isnanl_works=yes],
[gl_cv_func_isnanl_works=no],
- [case "$host_cpu" in
- # Guess no on ia64, x86_64, i386.
- ia64 | x86_64 | i*86) gl_cv_func_isnanl_works="guessing no";;
- *)
- case "$host_os" in
- netbsd*) gl_cv_func_isnanl_works="guessing no";;
- *) gl_cv_func_isnanl_works="guessing yes";;
- esac
- ;;
- esac
- ])
+ [gl_cv_func_isnanl_works="guessing yes"])
])
])
diff --git a/m4/langinfo_h.m4 b/m4/langinfo_h.m4
index e8d78f9d0..c3ecba66a 100644
--- a/m4/langinfo_h.m4
+++ b/m4/langinfo_h.m4
@@ -1,5 +1,5 @@
# langinfo_h.m4 serial 7
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/largefile.m4 b/m4/largefile.m4
index a1b564ad9..b7a6c48b6 100644
--- a/m4/largefile.m4
+++ b/m4/largefile.m4
@@ -1,6 +1,6 @@
# Enable large files on systems where this is not the default.
-# Copyright 1992-1996, 1998-2014 Free Software Foundation, Inc.
+# Copyright 1992-1996, 1998-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/ld-version-script.m4 b/m4/ld-version-script.m4
index f8b4a5c51..211d67b96 100644
--- a/m4/ld-version-script.m4
+++ b/m4/ld-version-script.m4
@@ -1,5 +1,5 @@
-# ld-version-script.m4 serial 3
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# ld-version-script.m4 serial 4
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -18,20 +18,18 @@ dnl From Simon Josefsson
AC_DEFUN([gl_LD_VERSION_SCRIPT],
[
AC_ARG_ENABLE([ld-version-script],
- AS_HELP_STRING([--enable-ld-version-script],
- [enable linker version script (default is enabled when possible)]),
- [have_ld_version_script=$enableval], [])
- if test -z "$have_ld_version_script"; then
- AC_MSG_CHECKING([if LD -Wl,--version-script works])
- save_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map"
- cat > conftest.map < conftest.map <conftest.map
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
+ [],
+ [cat > conftest.map <conftest.file
- if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
- AC_RUN_IFELSE(
- [AC_LANG_PROGRAM(
- [AC_INCLUDES_DEFAULT],
- [[struct stat sbuf;
- /* Linux will dereference the symlink and fail, as required by
- POSIX. That is better in the sense that it means we will not
- have to compile and use the lstat wrapper. */
- return lstat ("conftest.sym/", &sbuf) == 0;
- ]])],
- [gl_cv_func_lstat_dereferences_slashed_symlink=yes],
- [gl_cv_func_lstat_dereferences_slashed_symlink=no],
- [case "$host_os" in
- # Guess yes on glibc systems.
- *-gnu*) gl_cv_func_lstat_dereferences_slashed_symlink="guessing yes" ;;
- # If we don't know, assume the worst.
- *) gl_cv_func_lstat_dereferences_slashed_symlink="guessing no" ;;
- esac
- ])
- else
- # If the 'ln -s' command failed, then we probably don't even
- # have an lstat function.
- gl_cv_func_lstat_dereferences_slashed_symlink="guessing no"
- fi
+ AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM(
+ [AC_INCLUDES_DEFAULT],
+ [[struct stat sbuf;
+ if (symlink ("conftest.file", "conftest.sym") != 0)
+ return 1;
+ /* Linux will dereference the symlink and fail, as required by
+ POSIX. That is better in the sense that it means we will not
+ have to compile and use the lstat wrapper. */
+ return lstat ("conftest.sym/", &sbuf) == 0;
+ ]])],
+ [gl_cv_func_lstat_dereferences_slashed_symlink=yes],
+ [gl_cv_func_lstat_dereferences_slashed_symlink=no],
+ [case "$host_os" in
+ *-gnu*)
+ # Guess yes on glibc systems.
+ gl_cv_func_lstat_dereferences_slashed_symlink="guessing yes" ;;
+ *)
+ # If we don't know, assume the worst.
+ gl_cv_func_lstat_dereferences_slashed_symlink="guessing no" ;;
+ esac
+ ])
rm -f conftest.sym conftest.file
])
case "$gl_cv_func_lstat_dereferences_slashed_symlink" in
diff --git a/m4/malloc.m4 b/m4/malloc.m4
index 322ad6eff..31368ab97 100644
--- a/m4/malloc.m4
+++ b/m4/malloc.m4
@@ -1,5 +1,5 @@
# malloc.m4 serial 14
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/malloca.m4 b/m4/malloca.m4
index dcc1a0843..724895174 100644
--- a/m4/malloca.m4
+++ b/m4/malloca.m4
@@ -1,5 +1,5 @@
# malloca.m4 serial 1
-dnl Copyright (C) 2003-2004, 2006-2007, 2009-2014 Free Software Foundation,
+dnl Copyright (C) 2003-2004, 2006-2007, 2009-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/math_h.m4 b/m4/math_h.m4
index 9e2adfbac..7d0f58346 100644
--- a/m4/math_h.m4
+++ b/m4/math_h.m4
@@ -1,5 +1,5 @@
# math_h.m4 serial 114
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mathfunc.m4 b/m4/mathfunc.m4
index 6f0e6aacd..743bafbca 100644
--- a/m4/mathfunc.m4
+++ b/m4/mathfunc.m4
@@ -1,5 +1,5 @@
# mathfunc.m4 serial 11
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mbrtowc.m4 b/m4/mbrtowc.m4
index a9d157092..640579e61 100644
--- a/m4/mbrtowc.m4
+++ b/m4/mbrtowc.m4
@@ -1,5 +1,5 @@
-# mbrtowc.m4 serial 25
-dnl Copyright (C) 2001-2002, 2004-2005, 2008-2014 Free Software Foundation,
+# mbrtowc.m4 serial 26 -*- coding: utf-8 -*-
+dnl Copyright (C) 2001-2002, 2004-2005, 2008-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
@@ -39,6 +39,7 @@ AC_DEFUN([gl_FUNC_MBRTOWC],
gl_MBRTOWC_NULL_ARG2
gl_MBRTOWC_RETVAL
gl_MBRTOWC_NUL_RETVAL
+ gl_MBRTOWC_EMPTY_INPUT
case "$gl_cv_func_mbrtowc_null_arg1" in
*yes) ;;
*) AC_DEFINE([MBRTOWC_NULL_ARG1_BUG], [1],
@@ -67,6 +68,14 @@ AC_DEFUN([gl_FUNC_MBRTOWC],
REPLACE_MBRTOWC=1
;;
esac
+ case "$gl_cv_func_mbrtowc_empty_input" in
+ *yes) ;;
+ *) AC_DEFINE([MBRTOWC_EMPTY_INPUT_BUG], [1],
+ [Define if the mbrtowc function does not return (size_t) -2
+ for empty input.])
+ REPLACE_MBRTOWC=1
+ ;;
+ esac
fi
fi
])
@@ -533,6 +542,41 @@ int main ()
])
])
+dnl Test whether mbrtowc returns the correct value on empty input.
+
+AC_DEFUN([gl_MBRTOWC_EMPTY_INPUT],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether mbrtowc works on empty input],
+ [gl_cv_func_mbrtowc_empty_input],
+ [
+ dnl Initial guess, used when cross-compiling or when no suitable locale
+ dnl is present.
+changequote(,)dnl
+ case "$host_os" in
+ # Guess no on AIX and glibc systems.
+ aix* | *-gnu*)
+ gl_cv_func_mbrtowc_empty_input="guessing no" ;;
+ *) gl_cv_func_mbrtowc_empty_input="guessing yes" ;;
+ esac
+changequote([,])dnl
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include
+ static wchar_t wc;
+ static mbstate_t mbs;
+ int
+ main (void)
+ {
+ return mbrtowc (&wc, "", 0, &mbs) == (size_t) -2;
+ }]])],
+ [gl_cv_func_mbrtowc_empty_input=no],
+ [gl_cv_func_mbrtowc_empty_input=yes],
+ [:])
+ ])
+])
+
# Prerequisites of lib/mbrtowc.c.
AC_DEFUN([gl_PREREQ_MBRTOWC], [
:
diff --git a/m4/mbsinit.m4 b/m4/mbsinit.m4
index e1598a1d7..61c403290 100644
--- a/m4/mbsinit.m4
+++ b/m4/mbsinit.m4
@@ -1,5 +1,5 @@
# mbsinit.m4 serial 8
-dnl Copyright (C) 2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mbstate_t.m4 b/m4/mbstate_t.m4
index 068155a52..42ad6cd63 100644
--- a/m4/mbstate_t.m4
+++ b/m4/mbstate_t.m4
@@ -1,5 +1,5 @@
# mbstate_t.m4 serial 13
-dnl Copyright (C) 2000-2002, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2000-2002, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mbtowc.m4 b/m4/mbtowc.m4
index cacfe1610..88cdeeeff 100644
--- a/m4/mbtowc.m4
+++ b/m4/mbtowc.m4
@@ -1,5 +1,5 @@
# mbtowc.m4 serial 2
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/memchr.m4 b/m4/memchr.m4
index b9f126cfa..cb958d862 100644
--- a/m4/memchr.m4
+++ b/m4/memchr.m4
@@ -1,5 +1,5 @@
# memchr.m4 serial 12
-dnl Copyright (C) 2002-2004, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2004, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mkdir.m4 b/m4/mkdir.m4
index 51e78c13d..3d9868df1 100644
--- a/m4/mkdir.m4
+++ b/m4/mkdir.m4
@@ -1,6 +1,6 @@
# serial 11
-# Copyright (C) 2001, 2003-2004, 2006, 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2003-2004, 2006, 2008-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/mkstemp.m4 b/m4/mkstemp.m4
index 9033a4e60..f5df0d0b0 100644
--- a/m4/mkstemp.m4
+++ b/m4/mkstemp.m4
@@ -1,6 +1,6 @@
#serial 23
-# Copyright (C) 2001, 2003-2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2003-2007, 2009-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/mktime.m4 b/m4/mktime.m4
new file mode 100644
index 000000000..3f0e1eee4
--- /dev/null
+++ b/m4/mktime.m4
@@ -0,0 +1,253 @@
+# serial 25
+dnl Copyright (C) 2002-2003, 2005-2007, 2009-2015 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Jim Meyering.
+
+AC_DEFUN([gl_FUNC_MKTIME],
+[
+ AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
+
+ dnl We don't use AC_FUNC_MKTIME any more, because it is no longer maintained
+ dnl in Autoconf and because it invokes AC_LIBOBJ.
+ AC_CHECK_HEADERS_ONCE([unistd.h])
+ AC_CHECK_DECLS_ONCE([alarm])
+ AC_REQUIRE([gl_MULTIARCH])
+ if test $APPLE_UNIVERSAL_BUILD = 1; then
+ # A universal build on Apple Mac OS X platforms.
+ # The test result would be 'yes' in 32-bit mode and 'no' in 64-bit mode.
+ # But we need a configuration result that is valid in both modes.
+ gl_cv_func_working_mktime=no
+ fi
+ AC_CACHE_CHECK([for working mktime], [gl_cv_func_working_mktime],
+ [AC_RUN_IFELSE(
+ [AC_LANG_SOURCE(
+[[/* Test program from Paul Eggert and Tony Leneis. */
+#include
+#include
+#include
+
+#ifdef HAVE_UNISTD_H
+# include
+#endif
+
+#if HAVE_DECL_ALARM
+# include
+#endif
+
+/* Work around redefinition to rpl_putenv by other config tests. */
+#undef putenv
+
+static time_t time_t_max;
+static time_t time_t_min;
+
+/* Values we'll use to set the TZ environment variable. */
+static char *tz_strings[] = {
+ (char *) 0, "TZ=GMT0", "TZ=JST-9",
+ "TZ=EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00"
+};
+#define N_STRINGS (sizeof (tz_strings) / sizeof (tz_strings[0]))
+
+/* Return 0 if mktime fails to convert a date in the spring-forward gap.
+ Based on a problem report from Andreas Jaeger. */
+static int
+spring_forward_gap ()
+{
+ /* glibc (up to about 1998-10-07) failed this test. */
+ struct tm tm;
+
+ /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
+ instead of "TZ=America/Vancouver" in order to detect the bug even
+ on systems that don't support the Olson extension, or don't have the
+ full zoneinfo tables installed. */
+ putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");
+
+ tm.tm_year = 98;
+ tm.tm_mon = 3;
+ tm.tm_mday = 5;
+ tm.tm_hour = 2;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ return mktime (&tm) != (time_t) -1;
+}
+
+static int
+mktime_test1 (time_t now)
+{
+ struct tm *lt;
+ return ! (lt = localtime (&now)) || mktime (lt) == now;
+}
+
+static int
+mktime_test (time_t now)
+{
+ return (mktime_test1 (now)
+ && mktime_test1 ((time_t) (time_t_max - now))
+ && mktime_test1 ((time_t) (time_t_min + now)));
+}
+
+static int
+irix_6_4_bug ()
+{
+ /* Based on code from Ariel Faigon. */
+ struct tm tm;
+ tm.tm_year = 96;
+ tm.tm_mon = 3;
+ tm.tm_mday = 0;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ mktime (&tm);
+ return tm.tm_mon == 2 && tm.tm_mday == 31;
+}
+
+static int
+bigtime_test (int j)
+{
+ struct tm tm;
+ time_t now;
+ tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j;
+ now = mktime (&tm);
+ if (now != (time_t) -1)
+ {
+ struct tm *lt = localtime (&now);
+ if (! (lt
+ && lt->tm_year == tm.tm_year
+ && lt->tm_mon == tm.tm_mon
+ && lt->tm_mday == tm.tm_mday
+ && lt->tm_hour == tm.tm_hour
+ && lt->tm_min == tm.tm_min
+ && lt->tm_sec == tm.tm_sec
+ && lt->tm_yday == tm.tm_yday
+ && lt->tm_wday == tm.tm_wday
+ && ((lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst)
+ == (tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst))))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+year_2050_test ()
+{
+ /* The correct answer for 2050-02-01 00:00:00 in Pacific time,
+ ignoring leap seconds. */
+ unsigned long int answer = 2527315200UL;
+
+ struct tm tm;
+ time_t t;
+ tm.tm_year = 2050 - 1900;
+ tm.tm_mon = 2 - 1;
+ tm.tm_mday = 1;
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+
+ /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0"
+ instead of "TZ=America/Vancouver" in order to detect the bug even
+ on systems that don't support the Olson extension, or don't have the
+ full zoneinfo tables installed. */
+ putenv ("TZ=PST8PDT,M4.1.0,M10.5.0");
+
+ t = mktime (&tm);
+
+ /* Check that the result is either a failure, or close enough
+ to the correct answer that we can assume the discrepancy is
+ due to leap seconds. */
+ return (t == (time_t) -1
+ || (0 < t && answer - 120 <= t && t <= answer + 120));
+}
+
+int
+main ()
+{
+ int result = 0;
+ time_t t, delta;
+ int i, j;
+ int time_t_signed_magnitude = (time_t) ~ (time_t) 0 < (time_t) -1;
+ int time_t_signed = ! ((time_t) 0 < (time_t) -1);
+
+#if HAVE_DECL_ALARM
+ /* This test makes some buggy mktime implementations loop.
+ Give up after 60 seconds; a mktime slower than that
+ isn't worth using anyway. */
+ signal (SIGALRM, SIG_DFL);
+ alarm (60);
+#endif
+
+ time_t_max = (! time_t_signed
+ ? (time_t) -1
+ : ((((time_t) 1 << (sizeof (time_t) * CHAR_BIT - 2)) - 1)
+ * 2 + 1));
+ time_t_min = (! time_t_signed
+ ? (time_t) 0
+ : time_t_signed_magnitude
+ ? ~ (time_t) 0
+ : ~ time_t_max);
+
+ delta = time_t_max / 997; /* a suitable prime number */
+ for (i = 0; i < N_STRINGS; i++)
+ {
+ if (tz_strings[i])
+ putenv (tz_strings[i]);
+
+ for (t = 0; t <= time_t_max - delta && (result & 1) == 0; t += delta)
+ if (! mktime_test (t))
+ result |= 1;
+ if ((result & 2) == 0
+ && ! (mktime_test ((time_t) 1)
+ && mktime_test ((time_t) (60 * 60))
+ && mktime_test ((time_t) (60 * 60 * 24))))
+ result |= 2;
+
+ for (j = 1; (result & 4) == 0; j <<= 1)
+ {
+ if (! bigtime_test (j))
+ result |= 4;
+ if (INT_MAX / 2 < j)
+ break;
+ }
+ if ((result & 8) == 0 && ! bigtime_test (INT_MAX))
+ result |= 8;
+ }
+ if (! irix_6_4_bug ())
+ result |= 16;
+ if (! spring_forward_gap ())
+ result |= 32;
+ if (! year_2050_test ())
+ result |= 64;
+ return result;
+}]])],
+ [gl_cv_func_working_mktime=yes],
+ [gl_cv_func_working_mktime=no],
+ [gl_cv_func_working_mktime=no])
+ ])
+
+ if test $gl_cv_func_working_mktime = no; then
+ REPLACE_MKTIME=1
+ else
+ REPLACE_MKTIME=0
+ fi
+])
+
+AC_DEFUN([gl_FUNC_MKTIME_INTERNAL], [
+ AC_REQUIRE([gl_FUNC_MKTIME])
+ if test $REPLACE_MKTIME = 0; then
+ dnl BeOS has __mktime_internal in libc, but other platforms don't.
+ AC_CHECK_FUNC([__mktime_internal],
+ [AC_DEFINE([mktime_internal], [__mktime_internal],
+ [Define to the real name of the mktime_internal function.])
+ ],
+ [dnl mktime works but it doesn't export __mktime_internal,
+ dnl so we need to substitute our own mktime implementation.
+ REPLACE_MKTIME=1
+ ])
+ fi
+])
+
+# Prerequisites of lib/mktime.c.
+AC_DEFUN([gl_PREREQ_MKTIME], [:])
diff --git a/m4/mmap-anon.m4 b/m4/mmap-anon.m4
index 94ae2e2f2..92a88d05f 100644
--- a/m4/mmap-anon.m4
+++ b/m4/mmap-anon.m4
@@ -1,5 +1,5 @@
# mmap-anon.m4 serial 10
-dnl Copyright (C) 2005, 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005, 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/mode_t.m4 b/m4/mode_t.m4
index db6e192be..01badba7a 100644
--- a/m4/mode_t.m4
+++ b/m4/mode_t.m4
@@ -1,5 +1,5 @@
# mode_t.m4 serial 2
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/msvc-inval.m4 b/m4/msvc-inval.m4
index 7f26087e7..9446fa585 100644
--- a/m4/msvc-inval.m4
+++ b/m4/msvc-inval.m4
@@ -1,5 +1,5 @@
# msvc-inval.m4 serial 1
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/msvc-nothrow.m4 b/m4/msvc-nothrow.m4
index 9e32c171e..5d72a042d 100644
--- a/m4/msvc-nothrow.m4
+++ b/m4/msvc-nothrow.m4
@@ -1,5 +1,5 @@
# msvc-nothrow.m4 serial 1
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/multiarch.m4 b/m4/multiarch.m4
index 2cb956dee..fc575c1d4 100644
--- a/m4/multiarch.m4
+++ b/m4/multiarch.m4
@@ -1,5 +1,5 @@
# multiarch.m4 serial 7
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/netdb_h.m4 b/m4/netdb_h.m4
index cd7d48291..fce208014 100644
--- a/m4/netdb_h.m4
+++ b/m4/netdb_h.m4
@@ -1,5 +1,5 @@
# netdb_h.m4 serial 11
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/netinet_in_h.m4 b/m4/netinet_in_h.m4
index 1d447d6f1..42ac00844 100644
--- a/m4/netinet_in_h.m4
+++ b/m4/netinet_in_h.m4
@@ -1,5 +1,5 @@
# netinet_in_h.m4 serial 5
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/nl_langinfo.m4 b/m4/nl_langinfo.m4
index 6976e7767..c8bf20fb3 100644
--- a/m4/nl_langinfo.m4
+++ b/m4/nl_langinfo.m4
@@ -1,5 +1,5 @@
# nl_langinfo.m4 serial 5
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/nocrash.m4 b/m4/nocrash.m4
index 5a5d77d63..56283180f 100644
--- a/m4/nocrash.m4
+++ b/m4/nocrash.m4
@@ -1,5 +1,5 @@
# nocrash.m4 serial 4
-dnl Copyright (C) 2005, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/nproc.m4 b/m4/nproc.m4
index 937c4a920..988404b1c 100644
--- a/m4/nproc.m4
+++ b/m4/nproc.m4
@@ -1,5 +1,5 @@
# nproc.m4 serial 4
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/off_t.m4 b/m4/off_t.m4
index f5885b34b..0eb14678b 100644
--- a/m4/off_t.m4
+++ b/m4/off_t.m4
@@ -1,5 +1,5 @@
# off_t.m4 serial 1
-dnl Copyright (C) 2012-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2012-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/open.m4 b/m4/open.m4
index 68f116f0a..2accbaa92 100644
--- a/m4/open.m4
+++ b/m4/open.m4
@@ -1,5 +1,5 @@
# open.m4 serial 14
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/pathmax.m4 b/m4/pathmax.m4
index 114f91f04..0e3db7a23 100644
--- a/m4/pathmax.m4
+++ b/m4/pathmax.m4
@@ -1,5 +1,5 @@
# pathmax.m4 serial 10
-dnl Copyright (C) 2002-2003, 2005-2006, 2009-2014 Free Software Foundation,
+dnl Copyright (C) 2002-2003, 2005-2006, 2009-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/pipe.m4 b/m4/pipe.m4
index d3532d5dd..730c53439 100644
--- a/m4/pipe.m4
+++ b/m4/pipe.m4
@@ -1,5 +1,5 @@
# pipe.m4 serial 2
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/pipe2.m4 b/m4/pipe2.m4
index 1cff1fef0..0b64651a5 100644
--- a/m4/pipe2.m4
+++ b/m4/pipe2.m4
@@ -1,5 +1,5 @@
# pipe2.m4 serial 2
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/poll.m4 b/m4/poll.m4
index f523b1873..403d7d1fc 100644
--- a/m4/poll.m4
+++ b/m4/poll.m4
@@ -1,5 +1,5 @@
# poll.m4 serial 17
-dnl Copyright (c) 2003, 2005-2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (c) 2003, 2005-2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/poll_h.m4 b/m4/poll_h.m4
index fcfe7fa4d..662b60344 100644
--- a/m4/poll_h.m4
+++ b/m4/poll_h.m4
@@ -1,5 +1,5 @@
# poll_h.m4 serial 2
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/printf.m4 b/m4/printf.m4
index 9346ab041..d06746aae 100644
--- a/m4/printf.m4
+++ b/m4/printf.m4
@@ -1,5 +1,5 @@
-# printf.m4 serial 50
-dnl Copyright (C) 2003, 2007-2014 Free Software Foundation, Inc.
+# printf.m4 serial 52
+dnl Copyright (C) 2003, 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -61,7 +61,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_printf_sizes_c99="guessing yes";;
# Guess yes on FreeBSD >= 5.
- freebsd[1-4]*) gl_cv_func_printf_sizes_c99="guessing no";;
+ freebsd[1-4].*) gl_cv_func_printf_sizes_c99="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_printf_sizes_c99="guessing no";;
@@ -220,7 +220,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_printf_infinite="guessing yes";;
# Guess yes on FreeBSD >= 6.
- freebsd[1-5]*) gl_cv_func_printf_infinite="guessing no";;
+ freebsd[1-5].*) gl_cv_func_printf_infinite="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_printf_infinite="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_printf_infinite="guessing no";;
@@ -328,7 +328,7 @@ int main ()
# ifdef WORDS_BIGENDIAN
# define LDBL80_WORDS(exponent,manthi,mantlo) \
{ ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
- ((unsigned int) (manthi) << 16) | (unsigned int) (mantlo) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
(unsigned int) (mantlo) << 16 \
}
# else
@@ -365,66 +365,51 @@ int main ()
{ /* Pseudo-NaN. */
static union { unsigned int word[4]; long double value; } x =
{ LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
- if (sprintf (buf, "%Lf", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lf", x.value) <= 0)
result |= 4;
- if (sprintf (buf, "%Le", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Le", x.value) <= 0)
result |= 4;
- if (sprintf (buf, "%Lg", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lg", x.value) <= 0)
result |= 4;
}
{ /* Pseudo-Infinity. */
static union { unsigned int word[4]; long double value; } x =
{ LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
- if (sprintf (buf, "%Lf", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lf", x.value) <= 0)
result |= 8;
- if (sprintf (buf, "%Le", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Le", x.value) <= 0)
result |= 8;
- if (sprintf (buf, "%Lg", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lg", x.value) <= 0)
result |= 8;
}
{ /* Pseudo-Zero. */
static union { unsigned int word[4]; long double value; } x =
{ LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
- if (sprintf (buf, "%Lf", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lf", x.value) <= 0)
result |= 16;
- if (sprintf (buf, "%Le", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Le", x.value) <= 0)
result |= 16;
- if (sprintf (buf, "%Lg", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lg", x.value) <= 0)
result |= 16;
}
{ /* Unnormalized number. */
static union { unsigned int word[4]; long double value; } x =
{ LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
- if (sprintf (buf, "%Lf", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lf", x.value) <= 0)
result |= 32;
- if (sprintf (buf, "%Le", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Le", x.value) <= 0)
result |= 32;
- if (sprintf (buf, "%Lg", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lg", x.value) <= 0)
result |= 32;
}
{ /* Pseudo-Denormal. */
static union { unsigned int word[4]; long double value; } x =
{ LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
- if (sprintf (buf, "%Lf", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lf", x.value) <= 0)
result |= 64;
- if (sprintf (buf, "%Le", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Le", x.value) <= 0)
result |= 64;
- if (sprintf (buf, "%Lg", x.value) < 0
- || !strisnan (buf, 0, strlen (buf)))
+ if (sprintf (buf, "%Lg", x.value) <= 0)
result |= 64;
}
#endif
@@ -442,7 +427,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_printf_infinite_long_double="guessing yes";;
# Guess yes on FreeBSD >= 6.
- freebsd[1-5]*) gl_cv_func_printf_infinite_long_double="guessing no";;
+ freebsd[1-5].*) gl_cv_func_printf_infinite_long_double="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_printf_infinite_long_double="guessing yes";;
# Guess yes on HP-UX >= 11.
hpux[7-9]* | hpux10*) gl_cv_func_printf_infinite_long_double="guessing no";;
@@ -588,7 +573,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_printf_directive_f="guessing yes";;
# Guess yes on FreeBSD >= 6.
- freebsd[1-5]*) gl_cv_func_printf_directive_f="guessing no";;
+ freebsd[1-5].*) gl_cv_func_printf_directive_f="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_printf_directive_f="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_printf_directive_f="guessing no";;
@@ -1136,7 +1121,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
# Guess yes on FreeBSD >= 5.
- freebsd[1-4]*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ freebsd[1-4].*) gl_cv_func_snprintf_truncation_c99="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_snprintf_truncation_c99="guessing no";;
@@ -1235,7 +1220,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_snprintf_retval_c99="guessing yes";;
# Guess yes on FreeBSD >= 5.
- freebsd[1-4]*) gl_cv_func_snprintf_retval_c99="guessing no";;
+ freebsd[1-4].*) gl_cv_func_snprintf_retval_c99="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_snprintf_retval_c99="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_snprintf_retval_c99="guessing no";;
@@ -1316,7 +1301,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_snprintf_directive_n="guessing yes";;
# Guess yes on FreeBSD >= 5.
- freebsd[1-4]*) gl_cv_func_snprintf_directive_n="guessing no";;
+ freebsd[1-4].*) gl_cv_func_snprintf_directive_n="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_snprintf_directive_n="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_snprintf_directive_n="guessing no";;
@@ -1458,7 +1443,7 @@ changequote(,)dnl
# Guess yes on glibc systems.
*-gnu*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
# Guess yes on FreeBSD >= 5.
- freebsd[1-4]*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ freebsd[1-4].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
freebsd* | kfreebsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
# Guess yes on Mac OS X >= 10.3.
darwin[1-6].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
diff --git a/m4/putenv.m4 b/m4/putenv.m4
index d79321be9..73a5f4691 100644
--- a/m4/putenv.m4
+++ b/m4/putenv.m4
@@ -1,5 +1,5 @@
# putenv.m4 serial 20
-dnl Copyright (C) 2002-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/raise.m4 b/m4/raise.m4
index 8656578ef..ed6aae036 100644
--- a/m4/raise.m4
+++ b/m4/raise.m4
@@ -1,5 +1,5 @@
# raise.m4 serial 3
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/read.m4 b/m4/read.m4
index 176b0b04d..9fdd7df13 100644
--- a/m4/read.m4
+++ b/m4/read.m4
@@ -1,5 +1,5 @@
# read.m4 serial 4
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/readlink.m4 b/m4/readlink.m4
index f9ce868c2..88c9bfef7 100644
--- a/m4/readlink.m4
+++ b/m4/readlink.m4
@@ -1,5 +1,5 @@
# readlink.m4 serial 12
-dnl Copyright (C) 2003, 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003, 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/regex.m4 b/m4/regex.m4
index 08bd46a96..0fa7455df 100644
--- a/m4/regex.m4
+++ b/m4/regex.m4
@@ -1,6 +1,6 @@
# serial 65
-# Copyright (C) 1996-2001, 2003-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/m4/rename.m4 b/m4/rename.m4
index ea5779491..b5e433457 100644
--- a/m4/rename.m4
+++ b/m4/rename.m4
@@ -1,6 +1,6 @@
# serial 26
-# Copyright (C) 2001, 2003, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2003, 2005-2006, 2009-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/rmdir.m4 b/m4/rmdir.m4
index db6a9399c..09ed159e0 100644
--- a/m4/rmdir.m4
+++ b/m4/rmdir.m4
@@ -1,5 +1,5 @@
# rmdir.m4 serial 13
-dnl Copyright (C) 2002, 2005, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002, 2005, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/round.m4 b/m4/round.m4
index 13049b7cf..7fa1eb3fc 100644
--- a/m4/round.m4
+++ b/m4/round.m4
@@ -1,5 +1,5 @@
# round.m4 serial 16
-dnl Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/safe-read.m4 b/m4/safe-read.m4
index f0c42e08f..697a07cf8 100644
--- a/m4/safe-read.m4
+++ b/m4/safe-read.m4
@@ -1,5 +1,5 @@
# safe-read.m4 serial 6
-dnl Copyright (C) 2002-2003, 2005-2006, 2009-2014 Free Software Foundation,
+dnl Copyright (C) 2002-2003, 2005-2006, 2009-2015 Free Software Foundation,
dnl Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/safe-write.m4 b/m4/safe-write.m4
index 66648bbb5..1cef87c5a 100644
--- a/m4/safe-write.m4
+++ b/m4/safe-write.m4
@@ -1,5 +1,5 @@
# safe-write.m4 serial 4
-dnl Copyright (C) 2002, 2005-2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002, 2005-2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/secure_getenv.m4 b/m4/secure_getenv.m4
index 149888df4..6afe89fda 100644
--- a/m4/secure_getenv.m4
+++ b/m4/secure_getenv.m4
@@ -1,5 +1,5 @@
# Look up an environment variable more securely.
-dnl Copyright 2013-2014 Free Software Foundation, Inc.
+dnl Copyright 2013-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/select.m4 b/m4/select.m4
index 1d2fcb373..2a2ee6f90 100644
--- a/m4/select.m4
+++ b/m4/select.m4
@@ -1,5 +1,5 @@
# select.m4 serial 7
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/servent.m4 b/m4/servent.m4
index 4dc7a9f70..e871d45ce 100644
--- a/m4/servent.m4
+++ b/m4/servent.m4
@@ -1,5 +1,5 @@
# servent.m4 serial 2
-dnl Copyright (C) 2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/setenv.m4 b/m4/setenv.m4
index 0f46a7bec..3aa38d895 100644
--- a/m4/setenv.m4
+++ b/m4/setenv.m4
@@ -1,5 +1,5 @@
# setenv.m4 serial 26
-dnl Copyright (C) 2001-2004, 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2001-2004, 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/signal_h.m4 b/m4/signal_h.m4
index c8f664fbf..f737c36ba 100644
--- a/m4/signal_h.m4
+++ b/m4/signal_h.m4
@@ -1,5 +1,5 @@
# signal_h.m4 serial 18
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/signbit.m4 b/m4/signbit.m4
index 9ed48c780..21b9bf5e5 100644
--- a/m4/signbit.m4
+++ b/m4/signbit.m4
@@ -1,5 +1,5 @@
# signbit.m4 serial 13
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/size_max.m4 b/m4/size_max.m4
index 7e192d5e9..186e3fdda 100644
--- a/m4/size_max.m4
+++ b/m4/size_max.m4
@@ -1,5 +1,5 @@
# size_max.m4 serial 10
-dnl Copyright (C) 2003, 2005-2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003, 2005-2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/snprintf.m4 b/m4/snprintf.m4
index 888db35c0..8ae70050c 100644
--- a/m4/snprintf.m4
+++ b/m4/snprintf.m4
@@ -1,5 +1,5 @@
# snprintf.m4 serial 6
-dnl Copyright (C) 2002-2004, 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2004, 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/socketlib.m4 b/m4/socketlib.m4
index 041498baf..934173955 100644
--- a/m4/socketlib.m4
+++ b/m4/socketlib.m4
@@ -1,5 +1,5 @@
# socketlib.m4 serial 1
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sockets.m4 b/m4/sockets.m4
index da6ff7427..e75ac02b2 100644
--- a/m4/sockets.m4
+++ b/m4/sockets.m4
@@ -1,5 +1,5 @@
# sockets.m4 serial 7
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/socklen.m4 b/m4/socklen.m4
index 4c07f864c..bcabed3ef 100644
--- a/m4/socklen.m4
+++ b/m4/socklen.m4
@@ -1,5 +1,5 @@
# socklen.m4 serial 10
-dnl Copyright (C) 2005-2007, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005-2007, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sockpfaf.m4 b/m4/sockpfaf.m4
index 31d436f0e..50eb7619d 100644
--- a/m4/sockpfaf.m4
+++ b/m4/sockpfaf.m4
@@ -1,5 +1,5 @@
# sockpfaf.m4 serial 8
-dnl Copyright (C) 2004, 2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2004, 2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/ssize_t.m4 b/m4/ssize_t.m4
index fbe1d0687..25bd45143 100644
--- a/m4/ssize_t.m4
+++ b/m4/ssize_t.m4
@@ -1,5 +1,5 @@
# ssize_t.m4 serial 5 (gettext-0.18.2)
-dnl Copyright (C) 2001-2003, 2006, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2001-2003, 2006, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/stat-time.m4 b/m4/stat-time.m4
index ea5c4fc59..9c8ceec18 100644
--- a/m4/stat-time.m4
+++ b/m4/stat-time.m4
@@ -1,6 +1,6 @@
# Checks for stat-related time functions.
-# Copyright (C) 1998-1999, 2001, 2003, 2005-2007, 2009-2014 Free Software
+# Copyright (C) 1998-1999, 2001, 2003, 2005-2007, 2009-2015 Free Software
# Foundation, Inc.
# This file is free software; the Free Software Foundation
diff --git a/m4/stat.m4 b/m4/stat.m4
index 1ae327b36..d1b376896 100644
--- a/m4/stat.m4
+++ b/m4/stat.m4
@@ -1,6 +1,6 @@
# serial 11
-# Copyright (C) 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2009-2015 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/m4/stdalign.m4 b/m4/stdalign.m4
index 9efafe5c5..033b0d39e 100644
--- a/m4/stdalign.m4
+++ b/m4/stdalign.m4
@@ -1,6 +1,6 @@
# Check for stdalign.h that conforms to C11.
-dnl Copyright 2011-2014 Free Software Foundation, Inc.
+dnl Copyright 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -32,8 +32,12 @@ AC_DEFUN([gl_STDALIGN_H],
/* Test _Alignas only on platforms where gnulib can help. */
#if \
((defined __cplusplus && 201103 <= __cplusplus) \
- || __GNUC__ || __IBMC__ || __IBMCPP__ || __ICC \
- || 0x5110 <= __SUNPRO_C || 1300 <= _MSC_VER)
+ || (defined __APPLE__ && defined __MACH__ \
+ ? 4 < __GNUC__ + (1 <= __GNUC_MINOR__) \
+ : __GNUC__) \
+ || __HP_cc || __HP_aCC || __IBMC__ || __IBMCPP__ \
+ || __ICC || 0x5110 <= __SUNPRO_C \
+ || 1300 <= _MSC_VER)
struct alignas_test { char c; char alignas (8) alignas_8; };
char test_alignas[offsetof (struct alignas_test, alignas_8) == 8
? 1 : -1];
diff --git a/m4/stdbool.m4 b/m4/stdbool.m4
index 006ed52de..7273b8224 100644
--- a/m4/stdbool.m4
+++ b/m4/stdbool.m4
@@ -1,6 +1,6 @@
# Check for stdbool.h that conforms to C99.
-dnl Copyright (C) 2002-2006, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2006, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/stddef_h.m4 b/m4/stddef_h.m4
index c555e2952..231050274 100644
--- a/m4/stddef_h.m4
+++ b/m4/stddef_h.m4
@@ -1,6 +1,6 @@
-dnl A placeholder for POSIX 2008 , for platforms that have issues.
-# stddef_h.m4 serial 4
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl A placeholder for , for platforms that have issues.
+# stddef_h.m4 serial 5
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -10,6 +10,9 @@ AC_DEFUN([gl_STDDEF_H],
AC_REQUIRE([gl_STDDEF_H_DEFAULTS])
AC_REQUIRE([gt_TYPE_WCHAR_T])
STDDEF_H=
+ AC_CHECK_TYPE([max_align_t], [], [HAVE_MAX_ALIGN_T=0; STDDEF_H=stddef.h],
+ [[#include
+ ]])
if test $gt_cv_c_wchar_t = no; then
HAVE_WCHAR_T=0
STDDEF_H=stddef.h
@@ -43,5 +46,6 @@ AC_DEFUN([gl_STDDEF_H_DEFAULTS],
[
dnl Assume proper GNU behavior unless another module says otherwise.
REPLACE_NULL=0; AC_SUBST([REPLACE_NULL])
+ HAVE_MAX_ALIGN_T=1; AC_SUBST([HAVE_MAX_ALIGN_T])
HAVE_WCHAR_T=1; AC_SUBST([HAVE_WCHAR_T])
])
diff --git a/m4/stdint.m4 b/m4/stdint.m4
index 1981d9dbc..4011a4942 100644
--- a/m4/stdint.m4
+++ b/m4/stdint.m4
@@ -1,5 +1,5 @@
# stdint.m4 serial 43
-dnl Copyright (C) 2001-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2001-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4
index 7fc2ce9a8..5097c0b0d 100644
--- a/m4/stdint_h.m4
+++ b/m4/stdint_h.m4
@@ -1,5 +1,5 @@
# stdint_h.m4 serial 9
-dnl Copyright (C) 1997-2004, 2006, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 1997-2004, 2006, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/stdio_h.m4 b/m4/stdio_h.m4
index d15913a3c..f60cc2156 100644
--- a/m4/stdio_h.m4
+++ b/m4/stdio_h.m4
@@ -1,14 +1,41 @@
-# stdio_h.m4 serial 43
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# stdio_h.m4 serial 46
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
AC_DEFUN([gl_STDIO_H],
[
+ dnl For __USE_MINGW_ANSI_STDIO
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
AC_REQUIRE([gl_STDIO_H_DEFAULTS])
gl_NEXT_HEADERS([stdio.h])
+ dnl Determine whether __USE_MINGW_ANSI_STDIO makes printf and
+ dnl inttypes.h behave like gnu instead of system; we must give our
+ dnl printf wrapper the right attribute to match.
+ AC_CACHE_CHECK([which flavor of printf attribute matches inttypes macros],
+ [gl_cv_func_printf_attribute_flavor],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #define __STDC_FORMAT_MACROS 1
+ #include
+ #include
+ /* For non-mingw systems, compilation will trivially succeed.
+ For mingw, compilation will succeed for older mingw (system
+ printf, "I64d") and fail for newer mingw (gnu printf, "lld"). */
+ #if ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) && \
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
+ extern char PRIdMAX_probe[sizeof PRIdMAX == sizeof "I64d" ? 1 : -1];
+ #endif
+ ]])], [gl_cv_func_printf_attribute_flavor=system],
+ [gl_cv_func_printf_attribute_flavor=gnu])])
+ if test "$gl_cv_func_printf_attribute_flavor" = gnu; then
+ AC_DEFINE([GNULIB_PRINTF_ATTRIBUTE_FLAVOR_GNU], [1],
+ [Define to 1 if printf and friends should be labeled with
+ attribute "__gnu_printf__" instead of "__printf__"])
+ fi
+
dnl No need to create extra modules for these functions. Everyone who uses
dnl likely needs them.
GNULIB_FSCANF=1
diff --git a/m4/stdlib_h.m4 b/m4/stdlib_h.m4
index 03b448b94..0b4c623ec 100644
--- a/m4/stdlib_h.m4
+++ b/m4/stdlib_h.m4
@@ -1,5 +1,5 @@
# stdlib_h.m4 serial 42
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -55,6 +55,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
GNULIB_PTSNAME=0; AC_SUBST([GNULIB_PTSNAME])
GNULIB_PTSNAME_R=0; AC_SUBST([GNULIB_PTSNAME_R])
GNULIB_PUTENV=0; AC_SUBST([GNULIB_PUTENV])
+ GNULIB_QSORT_R=0; AC_SUBST([GNULIB_QSORT_R])
GNULIB_RANDOM=0; AC_SUBST([GNULIB_RANDOM])
GNULIB_RANDOM_R=0; AC_SUBST([GNULIB_RANDOM_R])
GNULIB_REALLOC_POSIX=0; AC_SUBST([GNULIB_REALLOC_POSIX])
@@ -107,6 +108,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS],
REPLACE_PTSNAME=0; AC_SUBST([REPLACE_PTSNAME])
REPLACE_PTSNAME_R=0; AC_SUBST([REPLACE_PTSNAME_R])
REPLACE_PUTENV=0; AC_SUBST([REPLACE_PUTENV])
+ REPLACE_QSORT_R=0; AC_SUBST([REPLACE_QSORT_R])
REPLACE_RANDOM_R=0; AC_SUBST([REPLACE_RANDOM_R])
REPLACE_REALLOC=0; AC_SUBST([REPLACE_REALLOC])
REPLACE_REALPATH=0; AC_SUBST([REPLACE_REALPATH])
diff --git a/m4/strdup.m4 b/m4/strdup.m4
index 1681a30eb..90ea29d22 100644
--- a/m4/strdup.m4
+++ b/m4/strdup.m4
@@ -1,6 +1,6 @@
# strdup.m4 serial 13
-dnl Copyright (C) 2002-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
diff --git a/m4/strftime.m4 b/m4/strftime.m4
index 0ba3dd074..4557626ae 100644
--- a/m4/strftime.m4
+++ b/m4/strftime.m4
@@ -1,6 +1,6 @@
# serial 33
-# Copyright (C) 1996-1997, 1999-2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 1996-1997, 1999-2007, 2009-2015 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
diff --git a/m4/string_h.m4 b/m4/string_h.m4
index 64e683f9d..55d09ef40 100644
--- a/m4/string_h.m4
+++ b/m4/string_h.m4
@@ -1,6 +1,6 @@
# Configure a GNU-like replacement for .
-# Copyright (C) 2007-2014 Free Software Foundation, Inc.
+# Copyright (C) 2007-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_file_h.m4 b/m4/sys_file_h.m4
index ad78efb9c..7b6a05a40 100644
--- a/m4/sys_file_h.m4
+++ b/m4/sys_file_h.m4
@@ -1,7 +1,7 @@
# Configure a replacement for .
# serial 6
-# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2008-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_select_h.m4 b/m4/sys_select_h.m4
index 1a502b4eb..5ec5919f2 100644
--- a/m4/sys_select_h.m4
+++ b/m4/sys_select_h.m4
@@ -1,5 +1,5 @@
# sys_select_h.m4 serial 20
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_socket_h.m4 b/m4/sys_socket_h.m4
index 114d82817..eaeabe7d9 100644
--- a/m4/sys_socket_h.m4
+++ b/m4/sys_socket_h.m4
@@ -1,5 +1,5 @@
# sys_socket_h.m4 serial 23
-dnl Copyright (C) 2005-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_stat_h.m4 b/m4/sys_stat_h.m4
index eaa7642ba..6c909e816 100644
--- a/m4/sys_stat_h.m4
+++ b/m4/sys_stat_h.m4
@@ -1,5 +1,5 @@
# sys_stat_h.m4 serial 28 -*- Autoconf -*-
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_time_h.m4 b/m4/sys_time_h.m4
index 5c79300f8..28c8b1acb 100644
--- a/m4/sys_time_h.m4
+++ b/m4/sys_time_h.m4
@@ -1,7 +1,7 @@
# Configure a replacement for .
# serial 8
-# Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -105,6 +105,7 @@ AC_DEFUN([gl_HEADER_SYS_TIME_H_DEFAULTS],
HAVE_GETTIMEOFDAY=1; AC_SUBST([HAVE_GETTIMEOFDAY])
HAVE_STRUCT_TIMEVAL=1; AC_SUBST([HAVE_STRUCT_TIMEVAL])
HAVE_SYS_TIME_H=1; AC_SUBST([HAVE_SYS_TIME_H])
+ HAVE_TIMEZONE_T=0; AC_SUBST([HAVE_TIMEZONE_T])
REPLACE_GETTIMEOFDAY=0; AC_SUBST([REPLACE_GETTIMEOFDAY])
REPLACE_STRUCT_TIMEVAL=0; AC_SUBST([REPLACE_STRUCT_TIMEVAL])
])
diff --git a/m4/sys_times_h.m4 b/m4/sys_times_h.m4
index fad63c4f0..fe927405c 100644
--- a/m4/sys_times_h.m4
+++ b/m4/sys_times_h.m4
@@ -1,7 +1,7 @@
# Configure a replacement for .
# serial 8
-# Copyright (C) 2008-2014 Free Software Foundation, Inc.
+# Copyright (C) 2008-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_types_h.m4 b/m4/sys_types_h.m4
index 9748905b5..2232aece6 100644
--- a/m4/sys_types_h.m4
+++ b/m4/sys_types_h.m4
@@ -1,5 +1,5 @@
# sys_types_h.m4 serial 5
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/sys_uio_h.m4 b/m4/sys_uio_h.m4
index ba6b4b5ed..3dfbcbe6e 100644
--- a/m4/sys_uio_h.m4
+++ b/m4/sys_uio_h.m4
@@ -1,5 +1,5 @@
# sys_uio_h.m4 serial 1
-dnl Copyright (C) 2011-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2011-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/tempname.m4 b/m4/tempname.m4
index 1594e1f5d..b1694d684 100644
--- a/m4/tempname.m4
+++ b/m4/tempname.m4
@@ -1,6 +1,6 @@
#serial 5
-# Copyright (C) 2006-2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2006-2007, 2009-2015 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
diff --git a/m4/time_h.m4 b/m4/time_h.m4
index 9852778f9..754b469a0 100644
--- a/m4/time_h.m4
+++ b/m4/time_h.m4
@@ -1,8 +1,8 @@
# Configure a more-standard replacement for .
-# Copyright (C) 2000-2001, 2003-2007, 2009-2014 Free Software Foundation, Inc.
+# Copyright (C) 2000-2001, 2003-2007, 2009-2015 Free Software Foundation, Inc.
-# serial 8
+# serial 9
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -26,7 +26,7 @@ AC_DEFUN([gl_HEADER_TIME_H_BODY],
])
dnl Check whether 'struct timespec' is declared
-dnl in time.h, sys/time.h, or pthread.h.
+dnl in time.h, sys/time.h, pthread.h, or unistd.h.
AC_DEFUN([gl_CHECK_TYPE_STRUCT_TIMESPEC],
[
@@ -44,6 +44,7 @@ AC_DEFUN([gl_CHECK_TYPE_STRUCT_TIMESPEC],
TIME_H_DEFINES_STRUCT_TIMESPEC=0
SYS_TIME_H_DEFINES_STRUCT_TIMESPEC=0
PTHREAD_H_DEFINES_STRUCT_TIMESPEC=0
+ UNISTD_H_DEFINES_STRUCT_TIMESPEC=0
if test $gl_cv_sys_struct_timespec_in_time_h = yes; then
TIME_H_DEFINES_STRUCT_TIMESPEC=1
else
@@ -70,12 +71,26 @@ AC_DEFUN([gl_CHECK_TYPE_STRUCT_TIMESPEC],
[gl_cv_sys_struct_timespec_in_pthread_h=no])])
if test $gl_cv_sys_struct_timespec_in_pthread_h = yes; then
PTHREAD_H_DEFINES_STRUCT_TIMESPEC=1
+ else
+ AC_CACHE_CHECK([for struct timespec in ],
+ [gl_cv_sys_struct_timespec_in_unistd_h],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include
+ ]],
+ [[static struct timespec x; x.tv_sec = x.tv_nsec;]])],
+ [gl_cv_sys_struct_timespec_in_unistd_h=yes],
+ [gl_cv_sys_struct_timespec_in_unistd_h=no])])
+ if test $gl_cv_sys_struct_timespec_in_unistd_h = yes; then
+ UNISTD_H_DEFINES_STRUCT_TIMESPEC=1
+ fi
fi
fi
fi
AC_SUBST([TIME_H_DEFINES_STRUCT_TIMESPEC])
AC_SUBST([SYS_TIME_H_DEFINES_STRUCT_TIMESPEC])
AC_SUBST([PTHREAD_H_DEFINES_STRUCT_TIMESPEC])
+ AC_SUBST([UNISTD_H_DEFINES_STRUCT_TIMESPEC])
])
AC_DEFUN([gl_TIME_MODULE_INDICATOR],
@@ -94,6 +109,7 @@ AC_DEFUN([gl_HEADER_TIME_H_DEFAULTS],
GNULIB_STRPTIME=0; AC_SUBST([GNULIB_STRPTIME])
GNULIB_TIMEGM=0; AC_SUBST([GNULIB_TIMEGM])
GNULIB_TIME_R=0; AC_SUBST([GNULIB_TIME_R])
+ GNULIB_TIME_RZ=0; AC_SUBST([GNULIB_TIME_RZ])
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_DECL_LOCALTIME_R=1; AC_SUBST([HAVE_DECL_LOCALTIME_R])
HAVE_NANOSLEEP=1; AC_SUBST([HAVE_NANOSLEEP])
diff --git a/m4/time_r.m4 b/m4/time_r.m4
index 7e15600f7..8df7e139c 100644
--- a/m4/time_r.m4
+++ b/m4/time_r.m4
@@ -1,6 +1,6 @@
dnl Reentrant time functions: localtime_r, gmtime_r.
-dnl Copyright (C) 2003, 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003, 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/time_rz.m4 b/m4/time_rz.m4
new file mode 100644
index 000000000..0c1f2c373
--- /dev/null
+++ b/m4/time_rz.m4
@@ -0,0 +1,21 @@
+dnl Time zone functions: tzalloc, localtime_rz, etc.
+
+dnl Copyright (C) 2015 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_TIME_RZ],
+[
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+ AC_REQUIRE([gl_HEADER_SYS_TIME_H_DEFAULTS])
+ AC_REQUIRE([AC_STRUCT_TIMEZONE])
+ AC_CHECK_FUNCS_ONCE([tzset])
+
+ AC_CHECK_TYPES([timezone_t], [], [], [[#include ]])
+ if test "$ac_cv_type_timezone_t" = yes; then
+ HAVE_TIMEZONE_T=1
+ fi
+])
diff --git a/m4/timegm.m4 b/m4/timegm.m4
new file mode 100644
index 000000000..8e68b99ba
--- /dev/null
+++ b/m4/timegm.m4
@@ -0,0 +1,26 @@
+# timegm.m4 serial 11
+dnl Copyright (C) 2003, 2007, 2009-2015 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_TIMEGM],
+[
+ AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS])
+ AC_REQUIRE([gl_FUNC_MKTIME])
+ REPLACE_TIMEGM=0
+ AC_CHECK_FUNCS_ONCE([timegm])
+ if test $ac_cv_func_timegm = yes; then
+ if test $gl_cv_func_working_mktime = no; then
+ # Assume that timegm is buggy if mktime is.
+ REPLACE_TIMEGM=1
+ fi
+ else
+ HAVE_TIMEGM=0
+ fi
+])
+
+# Prerequisites of lib/timegm.c.
+AC_DEFUN([gl_PREREQ_TIMEGM], [
+ :
+])
diff --git a/m4/times.m4 b/m4/times.m4
index 3ee364b8c..0359bbcee 100644
--- a/m4/times.m4
+++ b/m4/times.m4
@@ -1,5 +1,5 @@
# times.m4 serial 2
-dnl Copyright (C) 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/tm_gmtoff.m4 b/m4/tm_gmtoff.m4
index 486351b47..71a88f92f 100644
--- a/m4/tm_gmtoff.m4
+++ b/m4/tm_gmtoff.m4
@@ -1,5 +1,5 @@
# tm_gmtoff.m4 serial 3
-dnl Copyright (C) 2002, 2009-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002, 2009-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/trunc.m4 b/m4/trunc.m4
index ba87bd0c0..da276c1ff 100644
--- a/m4/trunc.m4
+++ b/m4/trunc.m4
@@ -1,5 +1,5 @@
# trunc.m4 serial 9
-dnl Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index 1fa197e69..b3c581f7b 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,5 +1,5 @@
-# unistd_h.m4 serial 67
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+# unistd_h.m4 serial 68
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
@@ -173,9 +173,11 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
REPLACE_PWRITE=0; AC_SUBST([REPLACE_PWRITE])
REPLACE_READ=0; AC_SUBST([REPLACE_READ])
REPLACE_READLINK=0; AC_SUBST([REPLACE_READLINK])
+ REPLACE_READLINKAT=0; AC_SUBST([REPLACE_READLINKAT])
REPLACE_RMDIR=0; AC_SUBST([REPLACE_RMDIR])
REPLACE_SLEEP=0; AC_SUBST([REPLACE_SLEEP])
REPLACE_SYMLINK=0; AC_SUBST([REPLACE_SYMLINK])
+ REPLACE_SYMLINKAT=0; AC_SUBST([REPLACE_SYMLINKAT])
REPLACE_TTYNAME_R=0; AC_SUBST([REPLACE_TTYNAME_R])
REPLACE_UNLINK=0; AC_SUBST([REPLACE_UNLINK])
REPLACE_UNLINKAT=0; AC_SUBST([REPLACE_UNLINKAT])
diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4
index 106192ea2..4708f2b8e 100644
--- a/m4/vasnprintf.m4
+++ b/m4/vasnprintf.m4
@@ -1,5 +1,5 @@
# vasnprintf.m4 serial 36
-dnl Copyright (C) 2002-2004, 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2004, 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/visibility.m4 b/m4/visibility.m4
index 552e39772..6fff7459c 100644
--- a/m4/visibility.m4
+++ b/m4/visibility.m4
@@ -1,5 +1,5 @@
# visibility.m4 serial 5 (gettext-0.18.2)
-dnl Copyright (C) 2005, 2008, 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2005, 2008, 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/vsnprintf.m4 b/m4/vsnprintf.m4
index 07f739df9..28be6fd18 100644
--- a/m4/vsnprintf.m4
+++ b/m4/vsnprintf.m4
@@ -1,5 +1,5 @@
# vsnprintf.m4 serial 6
-dnl Copyright (C) 2002-2004, 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2004, 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/warn-on-use.m4 b/m4/warn-on-use.m4
index cc690f8e2..1e98dc9b7 100644
--- a/m4/warn-on-use.m4
+++ b/m4/warn-on-use.m4
@@ -1,5 +1,5 @@
# warn-on-use.m4 serial 5
-dnl Copyright (C) 2010-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2010-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/warnings.m4 b/m4/warnings.m4
index 43156f450..5ae01def1 100644
--- a/m4/warnings.m4
+++ b/m4/warnings.m4
@@ -1,5 +1,5 @@
# warnings.m4 serial 11
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/wchar_h.m4 b/m4/wchar_h.m4
index 85db95286..9d1b0f8b6 100644
--- a/m4/wchar_h.m4
+++ b/m4/wchar_h.m4
@@ -1,6 +1,6 @@
dnl A placeholder for ISO C99 , for platforms that have issues.
-dnl Copyright (C) 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/wchar_t.m4 b/m4/wchar_t.m4
index 839a04c17..dc964e67e 100644
--- a/m4/wchar_t.m4
+++ b/m4/wchar_t.m4
@@ -1,5 +1,5 @@
# wchar_t.m4 serial 4 (gettext-0.18.2)
-dnl Copyright (C) 2002-2003, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2002-2003, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/wcrtomb.m4 b/m4/wcrtomb.m4
index 844ef6a8c..7e3fe3f54 100644
--- a/m4/wcrtomb.m4
+++ b/m4/wcrtomb.m4
@@ -1,5 +1,5 @@
# wcrtomb.m4 serial 11
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/wctype_h.m4 b/m4/wctype_h.m4
index 3fac0ee09..95a4705b0 100644
--- a/m4/wctype_h.m4
+++ b/m4/wctype_h.m4
@@ -2,7 +2,7 @@
dnl A placeholder for ISO C99 , for platforms that lack it.
-dnl Copyright (C) 2006-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2006-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/wint_t.m4 b/m4/wint_t.m4
index 9b07b0709..ca3fd449a 100644
--- a/m4/wint_t.m4
+++ b/m4/wint_t.m4
@@ -1,5 +1,5 @@
# wint_t.m4 serial 5 (gettext-0.18.2)
-dnl Copyright (C) 2003, 2007-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003, 2007-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/write.m4 b/m4/write.m4
index 820dd4f77..ce7042ef1 100644
--- a/m4/write.m4
+++ b/m4/write.m4
@@ -1,5 +1,5 @@
# write.m4 serial 5
-dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/m4/xsize.m4 b/m4/xsize.m4
index 3af23ec75..98faf7de0 100644
--- a/m4/xsize.m4
+++ b/m4/xsize.m4
@@ -1,5 +1,5 @@
# xsize.m4 serial 5
-dnl Copyright (C) 2003-2004, 2008-2014 Free Software Foundation, Inc.
+dnl Copyright (C) 2003-2004, 2008-2015 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
diff --git a/maint.mk b/maint.mk
index 30f2e8e69..893874b45 100644
--- a/maint.mk
+++ b/maint.mk
@@ -2,7 +2,7 @@
# This Makefile fragment tries to be general-purpose enough to be
# used by many projects via the gnulib maintainer-makefile module.
-## Copyright (C) 2001-2014 Free Software Foundation, Inc.
+## Copyright (C) 2001-2015 Free Software Foundation, Inc.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -21,13 +21,6 @@
# ME := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
ME := maint.mk
-# Diagnostic for continued use of deprecated variable.
-# Remove in 2013
-ifneq ($(build_aux),)
- $(error "$(ME): \
-set $$(_build-aux) relative to $$(srcdir) instead of $$(build_aux)")
-endif
-
# Helper variables.
_empty =
_sp = $(_empty) $(_empty)
@@ -155,6 +148,7 @@ export LC_ALL = C
## Sanity checks. ##
## --------------- ##
+ifneq ($(_gl-Makefile),)
_cfg_mk := $(wildcard $(srcdir)/cfg.mk)
# Collect the names of rules starting with 'sc_'.
@@ -196,6 +190,7 @@ local-check := \
$(filter-out $(local-checks-to-skip), $(local-checks-available)))
syntax-check: $(local-check)
+endif
# _sc_search_regexp
#
@@ -445,7 +440,7 @@ sc_require_config_h:
# You must include before including any other header file.
# This can possibly be via a package-specific header, if given by cfg.mk.
sc_require_config_h_first:
- @if $(VC_LIST_EXCEPT) | grep -l '\.c$$' > /dev/null; then \
+ @if $(VC_LIST_EXCEPT) | grep '\.c$$' > /dev/null; then \
fail=0; \
for i in $$($(VC_LIST_EXCEPT) | grep '\.c$$'); do \
grep '^# *include\>' $$i | $(SED) 1q \
@@ -469,7 +464,7 @@ sc_prohibit_HAVE_MBRTOWC:
define _sc_header_without_use
dummy=; : so we do not need a semicolon before each use; \
h_esc=`echo '[<"]'"$$h"'[">]'|$(SED) 's/\./\\\\./g'`; \
- if $(VC_LIST_EXCEPT) | grep -l '\.c$$' > /dev/null; then \
+ if $(VC_LIST_EXCEPT) | grep '\.c$$' > /dev/null; then \
files=$$(grep -l '^# *include '"$$h_esc" \
$$($(VC_LIST_EXCEPT) | grep '\.c$$')) && \
grep -LE "$$re" $$files | grep . && \
@@ -716,7 +711,7 @@ sc_changelog:
# Ensure that each .c file containing a "main" function also
# calls set_program_name.
sc_program_name:
- @require='set_program_name *\(m?argv\[0\]\);' \
+ @require='set_program_name *\(.*\);' \
in_vc_files='\.c$$' \
containing='\&2; exit 1; } || :
+# Except for shell files and for loops, double semicolon is probably a mistake
+sc_prohibit_double_semicolon:
+ @prohibit='; *;[ {} \]*(/[/*]|$$)' \
+ in_vc_files='\.[chly]$$' \
+ exclude='\bfor *\(.*\)' \
+ halt="Double semicolon detected" \
+ $(_sc_search_regexp)
+
_ptm1 = use "test C1 && test C2", not "test C1 -''a C2"
_ptm2 = use "test C1 || test C2", not "test C1 -''o C2"
# Using test's -a and -o operators is not portable.
@@ -1192,7 +1195,7 @@ sc_copyright_check:
in_vc_files=$(sample-test) \
halt='out of date copyright in $(sample-test); update it' \
$(_sc_search_regexp)
- @require='Copyright @copyright\{\} .*'$$(date +%Y)' Free' \
+ @require='Copyright @copyright\{\} .*'$$(date +%Y) \
in_vc_files=$(texi) \
halt='out of date copyright in $(texi); update it' \
$(_sc_search_regexp)
@@ -1597,7 +1600,7 @@ ifeq (a,b)
# do not need to be marked. Symbols matching '__.*' are
# reserved by the compiler, so are automatically excluded below.
_gl_TS_unmarked_extern_functions ?= main usage
-_gl_TS_function_match ?= /^(?:$(_gl_TS_extern)) +.*?(\S+) *\(/
+_gl_TS_function_match ?= /^(?:$(_gl_TS_extern)) +.*?(\w+) *\(/
# If your project uses a macro like "XTERN", then put
# the following in cfg.mk to override this default:
@@ -1630,6 +1633,7 @@ _gl_TS_other_headers ?= *.h
.PHONY: _gl_tight_scope
_gl_tight_scope: $(bin_PROGRAMS)
+ sed_wrap='s/^/^_?/;s/$$/$$/'; \
t=exceptions-$$$$; \
trap 's=$$?; rm -f $$t; exit $$s' 0; \
for sig in 1 2 3 13 15; do \
@@ -1639,20 +1643,20 @@ _gl_tight_scope: $(bin_PROGRAMS)
test -f $$f && d= || d=$(srcdir)/; echo $$d$$f; done`; \
hdr=`for f in $(_gl_TS_headers); do \
test -f $$f && d= || d=$(srcdir)/; echo $$d$$f; done`; \
- ( printf '^%s$$\n' '__.*' $(_gl_TS_unmarked_extern_functions); \
+ ( printf '%s\n' '__.*' $(_gl_TS_unmarked_extern_functions); \
grep -h -A1 '^extern .*[^;]$$' $$src \
- | grep -vE '^(extern |--)' | $(SED) 's/ .*//'; \
+ | grep -vE '^(extern |--|#)' | $(SED) 's/ .*//; /^$$/d'; \
perl -lne \
- '$(_gl_TS_function_match) and print "^$$1\$$"' $$hdr; \
- ) | sort -u > $$t; \
- nm -e $(_gl_TS_obj_files)|$(SED) -n 's/.* T //p'|grep -Ev -f $$t \
+ '$(_gl_TS_function_match) and print $$1' $$hdr; \
+ ) | sort -u | $(SED) "$$sed_wrap" > $$t; \
+ nm -g $(_gl_TS_obj_files)|$(SED) -n 's/.* T //p'|grep -Ev -f $$t \
&& { echo the above functions should have static scope >&2; \
exit 1; } || : ; \
- ( printf '^%s$$\n' '__.*' $(_gl_TS_unmarked_extern_vars); \
- perl -lne '$(_gl_TS_var_match) and print "^$$1\$$"' \
+ ( printf '%s\n' '__.*' main $(_gl_TS_unmarked_extern_vars); \
+ perl -lne '$(_gl_TS_var_match) and print $$1' \
$$hdr $(_gl_TS_other_headers) \
- ) | sort -u > $$t; \
- nm -e $(_gl_TS_obj_files) | $(SED) -n 's/.* [BCDGRS] //p' \
+ ) | sort -u | $(SED) "$$sed_wrap" > $$t; \
+ nm -g $(_gl_TS_obj_files) | $(SED) -n 's/.* [BCDGRS] //p' \
| sort -u | grep -Ev -f $$t \
&& { echo the above variables should have static scope >&2; \
exit 1; } || :
From f169be9fc8c0b8c9a27476b59e683c8dfeea8aa6 Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Thu, 22 Oct 2015 17:44:17 +0000
Subject: [PATCH 068/865] Wire up `guild compile -O0 foo.scm'
* module/scripts/compile.scm (%options): Resurrect -O option and make it
follow GCC, more or less. The default is equivalent to -O2.
* module/language/cps/compile-bytecode.scm (lower-cps):
* module/language/cps/optimize.scm (optimize-higher-order-cps): Move
split-rec to run unconditionally for now, as closure conversion fails
without it.
(define-optimizer): Only verify the result if we are debugging, to
save time.
(cps-default-optimization-options): New exported procedure.
* module/language/tree-il/optimize.scm
(tree-il-default-optimization-options): New exported procedure.
---
module/language/cps/compile-bytecode.scm | 6 ++
module/language/cps/optimize.scm | 29 +++++++--
module/language/tree-il/optimize.scm | 8 ++-
module/scripts/compile.scm | 77 +++++++++++++++++++++---
4 files changed, 105 insertions(+), 15 deletions(-)
diff --git a/module/language/cps/compile-bytecode.scm b/module/language/cps/compile-bytecode.scm
index 5b0c32990..86c9d307d 100644
--- a/module/language/cps/compile-bytecode.scm
+++ b/module/language/cps/compile-bytecode.scm
@@ -34,6 +34,7 @@
#:use-module (language cps optimize)
#:use-module (language cps reify-primitives)
#:use-module (language cps renumber)
+ #:use-module (language cps split-rec)
#:use-module (language cps intmap)
#:use-module (language cps intset)
#:use-module (system vm assembler)
@@ -513,6 +514,11 @@
env)))
(define (lower-cps exp opts)
+ ;; FIXME: For now the closure conversion pass relies on $rec instances
+ ;; being separated into SCCs. We should fix this to not be the case,
+ ;; and instead move the split-rec pass back to
+ ;; optimize-higher-order-cps.
+ (set! exp (split-rec exp))
(set! exp (optimize-higher-order-cps exp opts))
(set! exp (convert-closures exp))
(set! exp (optimize-first-order-cps exp opts))
diff --git a/module/language/cps/optimize.scm b/module/language/cps/optimize.scm
index c6576fc4a..8777222c9 100644
--- a/module/language/cps/optimize.scm
+++ b/module/language/cps/optimize.scm
@@ -37,11 +37,11 @@
#:use-module (language cps self-references)
#:use-module (language cps simplify)
#:use-module (language cps specialize-primcalls)
- #:use-module (language cps split-rec)
#:use-module (language cps type-fold)
#:use-module (language cps verify)
#:export (optimize-higher-order-cps
- optimize-first-order-cps))
+ optimize-first-order-cps
+ cps-default-optimization-options))
(define (kw-arg-ref args kw default)
(match (memq kw args)
@@ -75,8 +75,7 @@
(maybe-verify (pass program))
program))
...
- (verify program)
- program))
+ (maybe-verify program)))
;; Passes that are needed:
;;
@@ -84,7 +83,11 @@
;; calls, and eliding prompts if possible.
;;
(define-optimizer optimize-higher-order-cps
- (split-rec #:split-rec? #t)
+ ;; FIXME: split-rec call temporarily moved to compile-bytecode and run
+ ;; unconditionally, because closure conversion requires it. Move the
+ ;; pass back here when that's fixed.
+ ;;
+ ;; (split-rec #:split-rec? #t)
(eliminate-dead-code #:eliminate-dead-code? #t)
(prune-top-level-scopes #:prune-top-level-scopes? #t)
(simplify #:simplify? #t)
@@ -106,3 +109,19 @@
(eliminate-dead-code #:eliminate-dead-code? #t)
(rotate-loops #:rotate-loops? #t)
(simplify #:simplify? #t))
+
+(define (cps-default-optimization-options)
+ (list ;; #:split-rec? #t
+ #:eliminate-dead-code? #t
+ #:prune-top-level-scopes? #t
+ #:contify? #t
+ #:inline-constructors? #t
+ #:specialize-primcalls? #t
+ #:elide-values? #t
+ #:prune-bailouts? #t
+ #:peel-loops? #t
+ #:cse? #t
+ #:type-fold? #t
+ #:resolve-self-references? #t
+ #:licm? #t
+ #:rotate-loops? #t))
diff --git a/module/language/tree-il/optimize.scm b/module/language/tree-il/optimize.scm
index d5d4f43a0..8fa6a80e8 100644
--- a/module/language/tree-il/optimize.scm
+++ b/module/language/tree-il/optimize.scm
@@ -1,6 +1,6 @@
;;; Tree-il optimizer
-;; Copyright (C) 2009, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011, 2012, 2013, 2014, 2015 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
@@ -25,7 +25,8 @@
#:use-module (language tree-il fix-letrec)
#:use-module (language tree-il debug)
#:use-module (ice-9 match)
- #:export (optimize))
+ #:export (optimize
+ tree-il-default-optimization-options))
(define (optimize x env opts)
(let ((peval (match (memq #:partial-eval? opts)
@@ -37,3 +38,6 @@
(verify-tree-il
(peval (expand-primitives (resolve-primitives x env))
env)))))
+
+(define (tree-il-default-optimization-options)
+ '(#:partial-eval? #t))
diff --git a/module/scripts/compile.scm b/module/scripts/compile.scm
index 5b644c3d4..939fb2564 100644
--- a/module/scripts/compile.scm
+++ b/module/scripts/compile.scm
@@ -1,6 +1,6 @@
;;; Compile --- Command-line Guile Scheme compiler -*- coding: iso-8859-1 -*-
-;; Copyright 2005, 2008-2011, 2013, 2014 Free Software Foundation, Inc.
+;; Copyright 2005, 2008-2011, 2013, 2014, 2015 Free Software Foundation, Inc.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public License
@@ -32,10 +32,13 @@
#:use-module ((system base compile) #:select (compile-file))
#:use-module (system base target)
#:use-module (system base message)
+ #:use-module (language tree-il optimize)
+ #:use-module (language cps optimize)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-13)
#:use-module (srfi srfi-37)
#:use-module (ice-9 format)
+ #:use-module (ice-9 match)
#:export (compile))
(define %summary "Compile a file.")
@@ -45,6 +48,20 @@
(format (current-error-port) "error: ~{~a~}~%" messages)
(exit 1))
+(define (available-optimizations)
+ (append (tree-il-default-optimization-options)
+ (cps-default-optimization-options)))
+
+;; Turn on all optimizations unless -O0.
+(define (optimizations-for-level level)
+ (let lp ((options (available-optimizations)))
+ (match options
+ (() '())
+ ((#:partial-eval? val . options)
+ (cons* #:partial-eval? (> level 0) (lp options)))
+ ((kw val . options)
+ (cons* kw (> level 1) (lp options))))))
+
(define %options
;; Specifications of the command-line options.
(list (option '(#\h "help") #f #f
@@ -77,9 +94,28 @@
(cons (string->symbol arg) warnings)
(alist-delete 'warnings result))))))
- (option '(#\O "optimize") #f #f
+ (option '(#\O "optimize") #t #f
(lambda (opt name arg result)
- (alist-cons 'optimize? #t result)))
+ (define (return val)
+ (alist-cons 'optimizations val result))
+ (define (return-option name val)
+ (let ((kw (symbol->keyword
+ (string->symbol (string-append name "?")))))
+ (unless (memq kw (available-optimizations))
+ (fail "Unknown optimization pass `~a'" name))
+ (return (list kw val))))
+ (cond
+ ((string=? arg "help")
+ (show-optimization-help)
+ (exit 0))
+ ((equal? arg "0") (return (optimizations-for-level 0)))
+ ((equal? arg "1") (return (optimizations-for-level 1)))
+ ((equal? arg "2") (return (optimizations-for-level 2)))
+ ((equal? arg "3") (return (optimizations-for-level 3)))
+ ((string-prefix? "no-" arg)
+ (return-option (substring arg 3) #f))
+ (else
+ (return-option arg #t)))))
(option '(#\f "from") #t #f
(lambda (opt name arg result)
(if (assoc-ref result 'from)
@@ -129,15 +165,38 @@ There is NO WARRANTY, to the extent permitted by law.~%"))
%warning-types)
(format #t "~%"))
+(define (show-optimization-help)
+ (format #t "The available optimizations are:~%~%")
+ (let lp ((options (available-optimizations)))
+ (match options
+ (() #t)
+ ((kw val . options)
+ (let ((name (string-trim-right (symbol->string (keyword->symbol kw))
+ #\?)))
+ (format #t " -O~a~%"
+ (if val name (string-append "no-" name)))
+ (lp options)))))
+ (format #t "~%")
+ (format #t "To disable an optimization, prepend it with `no-', for example~%")
+ (format #t "`-Ono-cse.'~%~%")
+ (format #t "You may also specify optimization levels as `-O0', `-O1',~%")
+ (format #t "`-O2', or `-O3'. Currently `-O0' turns off all optimizations,~%")
+ (format #t "`-O1' turns on partial evaluation, and `-O2' and `-O3' turn on~%")
+ (format #t "everything. The default is equivalent to `-O2'.")
+ (format #t "~%"))
+
(define (compile . args)
(let* ((options (parse-args args))
(help? (assoc-ref options 'help?))
- (compile-opts (let ((o `(#:warnings
- ,(assoc-ref options 'warnings))))
- (if (assoc-ref options 'optimize?)
- (cons #:O o)
- o)))
+ (compile-opts `(#:warnings
+ ,(assoc-ref options 'warnings)
+ ,@(append-map
+ (lambda (opt)
+ (match opt
+ (('optimizations . opts) opts)
+ (_ '())))
+ options)))
(from (or (assoc-ref options 'from) 'scheme))
(to (or (assoc-ref options 'to) 'bytecode))
(target (or (assoc-ref options 'target) %host-type))
@@ -156,6 +215,8 @@ Compile each Guile source file FILE into a Guile object.
-W, --warn=WARNING emit warnings of type WARNING; use `--warn=help'
for a list of available warnings
+ -O, --optimize=OPT specify optimization passes to run; use `-Ohelp'
+ for a list of available optimizations
-f, --from=LANG specify a source language other than `scheme'
-t, --to=LANG specify a target language other than `bytecode'
From 5f4ac529e16f4d8cb6c5df61c5aa47b6384ac98f Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 23 Oct 2015 13:29:03 +0000
Subject: [PATCH 069/865] Use a bootstrapped -O0 compiler to compile the -O2
Guile
This reduces total build time to around 30 minutes or so.
* Makefile.am (SUBDIRS): Visit bootstrap/ before module/.
* bootstrap/Makefile.am: New file.
* configure.ac: Generate bootstrap/Makefile.
* meta/uninstalled-env.in (top_builddir): Add bootstrap/ to the
GUILE_LOAD_COMPILED_PATH.
* module/Makefile.am: Simplify to just sort files in alphabetical order;
since bootstrap/ was already compiled, we don't need to try to
optimize compilation order. Although the compiler will get faster as
more of the compiler itself is optimized, this isn't a significant
enough effect to worry about.
---
Makefile.am | 3 +-
bootstrap/Makefile.am | 152 +++++++++++
configure.ac | 1 +
meta/uninstalled-env.in | 4 +-
module/Makefile.am | 577 ++++++++++++++++++----------------------
5 files changed, 422 insertions(+), 315 deletions(-)
create mode 100644 bootstrap/Makefile.am
diff --git a/Makefile.am b/Makefile.am
index 8f9e014c7..7918c7974 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@
##
## Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2007,
## 2008, 2009, 2010, 2011, 2012, 2013,
-## 2014 Free Software Foundation, Inc.
+## 2014, 2015 Free Software Foundation, Inc.
##
## This file is part of GUILE.
##
@@ -30,6 +30,7 @@ SUBDIRS = \
lib \
meta \
libguile \
+ bootstrap \
module \
guile-readline \
examples \
diff --git a/bootstrap/Makefile.am b/bootstrap/Makefile.am
new file mode 100644
index 000000000..4f0bfac40
--- /dev/null
+++ b/bootstrap/Makefile.am
@@ -0,0 +1,152 @@
+## Process this file with automake to produce Makefile.in.
+##
+## Copyright (C) 2009, 2010, 2011, 2012, 2013,
+## 2014, 2015 Free Software Foundation, Inc.
+##
+## This file is part of GUILE.
+##
+## GUILE 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, or
+## (at your option) any later version.
+##
+## GUILE 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 GUILE; see the file COPYING.LESSER. If not,
+## write to the Free Software Foundation, Inc., 51 Franklin Street,
+## Fifth Floor, Boston, MA 02110-1301 USA
+
+
+GOBJECTS = $(SOURCES:%.scm=%.go)
+GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
+GUILE_OPTIMIZATIONS = -O1
+nobase_noinst_DATA = $(GOBJECTS) ice-9/eval.go
+CLEANFILES = $(GOBJECTS) ice-9/eval.go
+
+$(GOBJECTS): ice-9/eval.go
+
+VM_TARGETS := system/vm/assembler.go system/vm/disassembler.go
+$(VM_TARGETS): $(top_builddir)/libguile/vm-operations.h
+
+AM_V_GUILEC = $(AM_V_GUILEC_$(V))
+AM_V_GUILEC_ = $(AM_V_GUILEC_$(AM_DEFAULT_VERBOSITY))
+AM_V_GUILEC_0 = @echo " BOOTSTRAP GUILEC" $@;
+
+vpath %.scm @srcdir@/../module
+
+SUFFIXES = .scm .go
+
+.scm.go:
+ $(AM_V_GUILEC)GUILE_AUTO_COMPILE=0 \
+ $(top_builddir)/meta/uninstalled-env \
+ guild compile --target="$(host)" \
+ $(GUILE_WARNINGS) $(GUILE_OPTIMIZATIONS) \
+ -L "$(abs_srcdir)" -L "$(abs_builddir)" \
+ -L "$(abs_top_srcdir)/guile-readline" \
+ -o "$@" "$<"
+
+# A subset of sources that are used by the compiler. We can compile
+# these in any order; the order below is designed to hopefully result in
+# the lowest total compile time.
+SOURCES = \
+ language/cps/intmap.scm \
+ language/cps/intset.scm \
+ language/cps/utils.scm \
+ ice-9/vlist.scm \
+ srfi/srfi-1.scm \
+ \
+ language/tree-il.scm \
+ language/tree-il/analyze.scm \
+ language/tree-il/canonicalize.scm \
+ language/tree-il/compile-cps.scm \
+ language/tree-il/debug.scm \
+ language/tree-il/effects.scm \
+ language/tree-il/fix-letrec.scm \
+ language/tree-il/optimize.scm \
+ language/tree-il/peval.scm \
+ language/tree-il/primitives.scm \
+ language/tree-il/spec.scm \
+ \
+ language/cps.scm \
+ language/cps/closure-conversion.scm \
+ language/cps/compile-bytecode.scm \
+ language/cps/constructors.scm \
+ language/cps/contification.scm \
+ language/cps/cse.scm \
+ language/cps/dce.scm \
+ language/cps/effects-analysis.scm \
+ language/cps/elide-values.scm \
+ language/cps/licm.scm \
+ language/cps/peel-loops.scm \
+ language/cps/primitives.scm \
+ language/cps/prune-bailouts.scm \
+ language/cps/prune-top-level-scopes.scm \
+ language/cps/reify-primitives.scm \
+ language/cps/renumber.scm \
+ language/cps/rotate-loops.scm \
+ language/cps/optimize.scm \
+ language/cps/simplify.scm \
+ language/cps/self-references.scm \
+ language/cps/slot-allocation.scm \
+ language/cps/spec.scm \
+ language/cps/specialize-primcalls.scm \
+ language/cps/split-rec.scm \
+ language/cps/type-checks.scm \
+ language/cps/type-fold.scm \
+ language/cps/types.scm \
+ language/cps/verify.scm \
+ language/cps/with-cps.scm \
+ \
+ language/scheme/spec.scm \
+ language/scheme/compile-tree-il.scm \
+ language/scheme/decompile-tree-il.scm \
+ \
+ language/bytecode.scm \
+ language/bytecode/spec.scm \
+ \
+ language/value/spec.scm \
+ \
+ system/base/pmatch.scm \
+ system/base/syntax.scm \
+ system/base/compile.scm \
+ system/base/language.scm \
+ system/base/lalr.scm \
+ system/base/message.scm \
+ system/base/target.scm \
+ system/base/types.scm \
+ system/base/ck.scm \
+ \
+ ice-9/psyntax-pp.scm \
+ ice-9/boot-9.scm \
+ ice-9/r5rs.scm \
+ ice-9/deprecated.scm \
+ ice-9/binary-ports.scm \
+ ice-9/command-line.scm \
+ ice-9/control.scm \
+ ice-9/format.scm \
+ ice-9/getopt-long.scm \
+ ice-9/i18n.scm \
+ ice-9/match.scm \
+ ice-9/networking.scm \
+ ice-9/posix.scm \
+ ice-9/rdelim.scm \
+ ice-9/receive.scm \
+ ice-9/regex.scm \
+ ice-9/session.scm \
+ ice-9/pretty-print.scm \
+ \
+ system/vm/assembler.scm \
+ system/vm/debug.scm \
+ system/vm/disassembler.scm \
+ system/vm/dwarf.scm \
+ system/vm/elf.scm \
+ system/vm/frame.scm \
+ system/vm/linker.scm \
+ system/vm/loader.scm \
+ system/vm/program.scm \
+ system/vm/vm.scm \
+ system/foreign.scm
diff --git a/configure.ac b/configure.ac
index 19e00d818..9e639d6da 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1643,6 +1643,7 @@ AC_CONFIG_FILES([
test-suite/standalone/Makefile
test-suite/vm/Makefile
meta/Makefile
+ bootstrap/Makefile
module/Makefile
])
diff --git a/meta/uninstalled-env.in b/meta/uninstalled-env.in
index 567c6e243..f9f0dc78d 100644
--- a/meta/uninstalled-env.in
+++ b/meta/uninstalled-env.in
@@ -1,6 +1,6 @@
#!/bin/sh
-# Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation
+# Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011, 2012, 2015 Free Software Foundation
#
# This file is part of GUILE.
#
@@ -84,7 +84,7 @@ then
then
GUILE_LOAD_COMPILED_PATH="${top_builddir}/module:${top_builddir}/guile-readline"
else
- for d in "/module" "/guile-readline"
+ for d in "/module" "/bootstrap" "/guile-readline"
do
# This hair prevents double inclusion.
# The ":" prevents prefix aliasing.
diff --git a/module/Makefile.am b/module/Makefile.am
index 17e663241..f835ceb73 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -25,74 +25,13 @@ include $(top_srcdir)/am/guilec
# We're at the root of the module hierarchy.
modpath =
-# Build eval.go first. Then build psyntax-pp.go, as the expander has to
-# run on every loaded scheme file. It doesn't pay off at compile-time
-# to interpret the expander in parallel.
-BOOT_SOURCES = ice-9/psyntax-pp.scm
-BOOT_GOBJECTS = $(BOOT_SOURCES:%.scm=%.go)
-$(BOOT_GOBJECTS): ice-9/eval.go
-$(GOBJECTS): $(BOOT_GOBJECTS)
-CLEANFILES += ice-9/eval.go $(BOOT_GOBJECTS)
-nobase_mod_DATA += ice-9/eval.scm $(BOOT_SOURCES)
-nobase_ccache_DATA += ice-9/eval.go $(BOOT_GOBJECTS)
-EXTRA_DIST += ice-9/eval.scm $(BOOT_SOURCES)
-ETAGS_ARGS += ice-9/eval.scm $(BOOT_SOURCES)
-
VM_TARGETS := system/vm/assembler.go system/vm/disassembler.go
$(VM_TARGETS): $(top_builddir)/libguile/vm-operations.h
ice-9/boot-9.go: ice-9/boot-9.scm ice-9/quasisyntax.scm ice-9/r6rs-libraries.scm
ice-9/match.go: ice-9/match.scm ice-9/match.upstream.scm
-
-# We can compile these in any order, but it's fastest if we compile
-# boot-9 first, then the compiler itself, then the rest of the code.
-SOURCES = \
- ice-9/boot-9.scm \
- language/cps/intmap.scm \
- language/cps/intset.scm \
- language/tree-il/peval.scm \
- system/vm/elf.scm \
- ice-9/vlist.scm \
- srfi/srfi-1.scm \
- system/vm/linker.scm \
- system/vm/dwarf.scm \
- system/vm/assembler.scm \
- \
- language/tree-il.scm \
- $(TREE_IL_LANG_SOURCES) \
- $(CPS_LANG_SOURCES) \
- $(BYTECODE_LANG_SOURCES) \
- $(VALUE_LANG_SOURCES) \
- $(SCHEME_LANG_SOURCES) \
- $(SYSTEM_BASE_SOURCES) \
- \
- $(ICE_9_SOURCES) \
- $(SYSTEM_SOURCES) \
- $(SRFI_SOURCES) \
- $(RNRS_SOURCES) \
- $(OOP_SOURCES) \
- $(SCRIPTS_SOURCES) \
- $(ECMASCRIPT_LANG_SOURCES) \
- $(ELISP_LANG_SOURCES) \
- $(BRAINFUCK_LANG_SOURCES) \
- $(LIB_SOURCES) \
- $(WEB_SOURCES)
-
-## test.scm is not currently installed.
-EXTRA_DIST += \
- ice-9/test.scm \
- ice-9/compile-psyntax.scm \
- ice-9/ChangeLog-2008
-ETAGS_ARGS += \
- ice-9/test.scm \
- ice-9/compile-psyntax.scm \
- ice-9/ChangeLog-2008
-
-ice-9/psyntax-pp.scm.gen:
- $(top_builddir_absolute)/meta/guile --no-auto-compile -s $(srcdir)/ice-9/compile-psyntax.scm \
- $(srcdir)/ice-9/psyntax.scm $(srcdir)/ice-9/psyntax-pp.scm
-
-.PHONY: ice-9/psyntax-pp.scm.gen
+srfi/srfi-64.go: srfi/srfi-64.scm srfi/srfi-64/testing.scm
+$(nobase_ccache_DATA): ../bootstrap/ice-9/eval.go
# Keep this rule in sync with that in `am/guilec'.
ice-9/psyntax-pp.go: ice-9/psyntax.scm ice-9/psyntax-pp.scm
@@ -103,62 +42,121 @@ ice-9/psyntax-pp.go: ice-9/psyntax.scm ice-9/psyntax-pp.scm
-L "$(abs_top_srcdir)/guile-readline" \
-o "ice-9/psyntax-pp.go" "$(srcdir)/ice-9/psyntax.scm"
-SCHEME_LANG_SOURCES = \
- language/scheme/spec.scm \
- language/scheme/compile-tree-il.scm \
- language/scheme/decompile-tree-il.scm
-
-TREE_IL_LANG_SOURCES = \
- language/tree-il/primitives.scm \
- language/tree-il/effects.scm \
- language/tree-il/fix-letrec.scm \
- language/tree-il/optimize.scm \
- language/tree-il/canonicalize.scm \
- language/tree-il/analyze.scm \
- language/tree-il/compile-cps.scm \
- language/tree-il/debug.scm \
- language/tree-il/spec.scm
-
-CPS_LANG_SOURCES = \
- language/cps.scm \
- language/cps/closure-conversion.scm \
- language/cps/compile-bytecode.scm \
- language/cps/constructors.scm \
- language/cps/contification.scm \
- language/cps/cse.scm \
- language/cps/dce.scm \
- language/cps/effects-analysis.scm \
- language/cps/elide-values.scm \
- language/cps/licm.scm \
- language/cps/peel-loops.scm \
- language/cps/primitives.scm \
- language/cps/prune-bailouts.scm \
- language/cps/prune-top-level-scopes.scm \
- language/cps/reify-primitives.scm \
- language/cps/renumber.scm \
- language/cps/rotate-loops.scm \
- language/cps/optimize.scm \
- language/cps/simplify.scm \
- language/cps/self-references.scm \
- language/cps/slot-allocation.scm \
- language/cps/spec.scm \
- language/cps/specialize-primcalls.scm \
- language/cps/split-rec.scm \
- language/cps/type-checks.scm \
- language/cps/type-fold.scm \
- language/cps/types.scm \
- language/cps/utils.scm \
- language/cps/verify.scm \
- language/cps/with-cps.scm
-
-BYTECODE_LANG_SOURCES = \
- language/bytecode.scm \
- language/bytecode/spec.scm
-
-VALUE_LANG_SOURCES = \
- language/value/spec.scm
-
-ECMASCRIPT_LANG_SOURCES = \
+SOURCES = \
+ ice-9/and-let-star.scm \
+ ice-9/binary-ports.scm \
+ ice-9/boot-9.scm \
+ ice-9/buffered-input.scm \
+ ice-9/calling.scm \
+ ice-9/channel.scm \
+ ice-9/command-line.scm \
+ ice-9/common-list.scm \
+ ice-9/control.scm \
+ ice-9/curried-definitions.scm \
+ ice-9/debug.scm \
+ ice-9/deprecated.scm \
+ ice-9/documentation.scm \
+ ice-9/eval-string.scm \
+ ice-9/eval.scm \
+ ice-9/expect.scm \
+ ice-9/format.scm \
+ ice-9/ftw.scm \
+ ice-9/futures.scm \
+ ice-9/gap-buffer.scm \
+ ice-9/getopt-long.scm \
+ ice-9/hash-table.scm \
+ ice-9/hcons.scm \
+ ice-9/history.scm \
+ ice-9/i18n.scm \
+ ice-9/iconv.scm \
+ ice-9/lineio.scm \
+ ice-9/list.scm \
+ ice-9/local-eval.scm \
+ ice-9/ls.scm \
+ ice-9/mapping.scm \
+ ice-9/match.scm \
+ ice-9/networking.scm \
+ ice-9/null.scm \
+ ice-9/occam-channel.scm \
+ ice-9/optargs.scm \
+ ice-9/peg.scm \
+ ice-9/peg/cache.scm \
+ ice-9/peg/codegen.scm \
+ ice-9/peg/simplify-tree.scm \
+ ice-9/peg/string-peg.scm \
+ ice-9/peg/using-parsers.scm \
+ ice-9/poe.scm \
+ ice-9/poll.scm \
+ ice-9/popen.scm \
+ ice-9/posix.scm \
+ ice-9/pretty-print.scm \
+ ice-9/psyntax-pp.scm \
+ ice-9/q.scm \
+ ice-9/r5rs.scm \
+ ice-9/rdelim.scm \
+ ice-9/receive.scm \
+ ice-9/regex.scm \
+ ice-9/runq.scm \
+ ice-9/rw.scm \
+ ice-9/safe-r5rs.scm \
+ ice-9/safe.scm \
+ ice-9/save-stack.scm \
+ ice-9/scm-style-repl.scm \
+ ice-9/serialize.scm \
+ ice-9/session.scm \
+ ice-9/slib.scm \
+ ice-9/stack-catch.scm \
+ ice-9/streams.scm \
+ ice-9/string-fun.scm \
+ ice-9/syncase.scm \
+ ice-9/threads.scm \
+ ice-9/time.scm \
+ ice-9/top-repl.scm \
+ ice-9/unicode.scm \
+ ice-9/vlist.scm \
+ ice-9/weak-vector.scm \
+ \
+ language/brainfuck/parse.scm \
+ language/brainfuck/compile-scheme.scm \
+ language/brainfuck/compile-tree-il.scm \
+ language/brainfuck/spec.scm \
+ \
+ language/bytecode.scm \
+ language/bytecode/spec.scm \
+ \
+ language/cps.scm \
+ language/cps/closure-conversion.scm \
+ language/cps/compile-bytecode.scm \
+ language/cps/constructors.scm \
+ language/cps/contification.scm \
+ language/cps/cse.scm \
+ language/cps/dce.scm \
+ language/cps/effects-analysis.scm \
+ language/cps/elide-values.scm \
+ language/cps/intmap.scm \
+ language/cps/intset.scm \
+ language/cps/licm.scm \
+ language/cps/optimize.scm \
+ language/cps/peel-loops.scm \
+ language/cps/primitives.scm \
+ language/cps/prune-bailouts.scm \
+ language/cps/prune-top-level-scopes.scm \
+ language/cps/reify-primitives.scm \
+ language/cps/renumber.scm \
+ language/cps/rotate-loops.scm \
+ language/cps/self-references.scm \
+ language/cps/simplify.scm \
+ language/cps/slot-allocation.scm \
+ language/cps/spec.scm \
+ language/cps/specialize-primcalls.scm \
+ language/cps/split-rec.scm \
+ language/cps/type-checks.scm \
+ language/cps/type-fold.scm \
+ language/cps/types.scm \
+ language/cps/utils.scm \
+ language/cps/verify.scm \
+ language/cps/with-cps.scm \
+ \
language/ecmascript/tokenize.scm \
language/ecmascript/parse.scm \
language/ecmascript/impl.scm \
@@ -166,9 +164,8 @@ ECMASCRIPT_LANG_SOURCES = \
language/ecmascript/function.scm \
language/ecmascript/array.scm \
language/ecmascript/compile-tree-il.scm \
- language/ecmascript/spec.scm
-
-ELISP_LANG_SOURCES = \
+ language/ecmascript/spec.scm \
+ \
language/elisp/falias.scm \
language/elisp/lexer.scm \
language/elisp/parser.scm \
@@ -177,157 +174,26 @@ ELISP_LANG_SOURCES = \
language/elisp/runtime.scm \
language/elisp/runtime/function-slot.scm \
language/elisp/runtime/value-slot.scm \
- language/elisp/spec.scm
-
-BRAINFUCK_LANG_SOURCES = \
- language/brainfuck/parse.scm \
- language/brainfuck/compile-scheme.scm \
- language/brainfuck/compile-tree-il.scm \
- language/brainfuck/spec.scm
-
-SCRIPTS_SOURCES = \
- scripts/compile.scm \
- scripts/disassemble.scm \
- scripts/display-commentary.scm \
- scripts/doc-snarf.scm \
- scripts/frisk.scm \
- scripts/generate-autoload.scm \
- scripts/help.scm \
- scripts/lint.scm \
- scripts/list.scm \
- scripts/punify.scm \
- scripts/read-scheme-source.scm \
- scripts/read-text-outline.scm \
- scripts/use2dot.scm \
- scripts/snarf-check-and-output-texi.scm \
- scripts/summarize-guile-TODO.scm \
- scripts/api-diff.scm \
- scripts/read-rfc822.scm \
- scripts/snarf-guile-m4-docs.scm \
- scripts/autofrisk.scm \
- scripts/scan-api.scm
-
-SYSTEM_BASE_SOURCES = \
- system/base/pmatch.scm \
- system/base/syntax.scm \
- system/base/compile.scm \
- system/base/language.scm \
- system/base/lalr.scm \
- system/base/message.scm \
- system/base/target.scm \
- system/base/types.scm \
- system/base/ck.scm
-
-ICE_9_SOURCES = \
- ice-9/r5rs.scm \
- ice-9/deprecated.scm \
- ice-9/and-let-star.scm \
- ice-9/binary-ports.scm \
- ice-9/calling.scm \
- ice-9/command-line.scm \
- ice-9/common-list.scm \
- ice-9/control.scm \
- ice-9/curried-definitions.scm \
- ice-9/debug.scm \
- ice-9/documentation.scm \
- ice-9/eval-string.scm \
- ice-9/expect.scm \
- ice-9/format.scm \
- ice-9/futures.scm \
- ice-9/getopt-long.scm \
- ice-9/hash-table.scm \
- ice-9/hcons.scm \
- ice-9/i18n.scm \
- ice-9/iconv.scm \
- ice-9/lineio.scm \
- ice-9/ls.scm \
- ice-9/mapping.scm \
- ice-9/match.scm \
- ice-9/networking.scm \
- ice-9/null.scm \
- ice-9/occam-channel.scm \
- ice-9/optargs.scm \
- ice-9/peg/simplify-tree.scm \
- ice-9/peg/codegen.scm \
- ice-9/peg/cache.scm \
- ice-9/peg/using-parsers.scm \
- ice-9/peg/string-peg.scm \
- ice-9/peg.scm \
- ice-9/poe.scm \
- ice-9/poll.scm \
- ice-9/popen.scm \
- ice-9/posix.scm \
- ice-9/q.scm \
- ice-9/rdelim.scm \
- ice-9/receive.scm \
- ice-9/regex.scm \
- ice-9/runq.scm \
- ice-9/rw.scm \
- ice-9/safe-r5rs.scm \
- ice-9/safe.scm \
- ice-9/save-stack.scm \
- ice-9/scm-style-repl.scm \
- ice-9/session.scm \
- ice-9/slib.scm \
- ice-9/stack-catch.scm \
- ice-9/streams.scm \
- ice-9/string-fun.scm \
- ice-9/syncase.scm \
- ice-9/threads.scm \
- ice-9/top-repl.scm \
- ice-9/buffered-input.scm \
- ice-9/time.scm \
- ice-9/history.scm \
- ice-9/channel.scm \
- ice-9/pretty-print.scm \
- ice-9/ftw.scm \
- ice-9/gap-buffer.scm \
- ice-9/weak-vector.scm \
- ice-9/list.scm \
- ice-9/serialize.scm \
- ice-9/local-eval.scm \
- ice-9/unicode.scm
-
-srfi/srfi-64.go: srfi/srfi-64.scm srfi/srfi-64/testing.scm
-
-SRFI_SOURCES = \
- srfi/srfi-2.scm \
- srfi/srfi-4.scm \
- srfi/srfi-4/gnu.scm \
- srfi/srfi-6.scm \
- srfi/srfi-8.scm \
- srfi/srfi-9.scm \
- srfi/srfi-9/gnu.scm \
- srfi/srfi-10.scm \
- srfi/srfi-11.scm \
- srfi/srfi-13.scm \
- srfi/srfi-14.scm \
- srfi/srfi-16.scm \
- srfi/srfi-17.scm \
- srfi/srfi-18.scm \
- srfi/srfi-19.scm \
- srfi/srfi-26.scm \
- srfi/srfi-27.scm \
- srfi/srfi-28.scm \
- srfi/srfi-31.scm \
- srfi/srfi-34.scm \
- srfi/srfi-35.scm \
- srfi/srfi-37.scm \
- srfi/srfi-38.scm \
- srfi/srfi-41.scm \
- srfi/srfi-42.scm \
- srfi/srfi-43.scm \
- srfi/srfi-39.scm \
- srfi/srfi-45.scm \
- srfi/srfi-60.scm \
- srfi/srfi-64.scm \
- srfi/srfi-67.scm \
- srfi/srfi-69.scm \
- srfi/srfi-88.scm \
- srfi/srfi-98.scm \
- srfi/srfi-111.scm
-
-RNRS_SOURCES = \
+ language/elisp/spec.scm \
+ \
+ language/scheme/compile-tree-il.scm \
+ language/scheme/decompile-tree-il.scm \
+ language/scheme/spec.scm \
+ \
+ language/tree-il.scm \
+ language/tree-il/analyze.scm \
+ language/tree-il/canonicalize.scm \
+ language/tree-il/compile-cps.scm \
+ language/tree-il/debug.scm \
+ language/tree-il/effects.scm \
+ language/tree-il/fix-letrec.scm \
+ language/tree-il/optimize.scm \
+ language/tree-il/peval.scm \
+ language/tree-il/primitives.scm \
+ language/tree-il/spec.scm \
+ \
+ language/value/spec.scm \
+ \
rnrs/base.scm \
rnrs/conditions.scm \
rnrs/control.scm \
@@ -353,47 +219,118 @@ RNRS_SOURCES = \
rnrs/records/inspection.scm \
rnrs/records/procedural.scm \
rnrs/records/syntactic.scm \
- rnrs.scm
-
-EXTRA_DIST += scripts/ChangeLog-2008
-EXTRA_DIST += scripts/README
-
-OOP_SOURCES = \
- oop/goops.scm \
- oop/goops/active-slot.scm \
- oop/goops/composite-slot.scm \
- oop/goops/describe.scm \
- oop/goops/internal.scm \
- oop/goops/save.scm \
- oop/goops/stklos.scm \
- oop/goops/accessors.scm \
- oop/goops/simple.scm
-
-SYSTEM_SOURCES = \
- system/vm/inspect.scm \
- system/vm/coverage.scm \
- system/vm/frame.scm \
- system/vm/loader.scm \
- system/vm/program.scm \
- system/vm/trace.scm \
- system/vm/traps.scm \
- system/vm/trap-state.scm \
- system/vm/debug.scm \
- system/vm/disassembler.scm \
- system/vm/vm.scm \
+ rnrs.scm \
+ \
+ oop/goops.scm \
+ oop/goops/active-slot.scm \
+ oop/goops/composite-slot.scm \
+ oop/goops/describe.scm \
+ oop/goops/internal.scm \
+ oop/goops/save.scm \
+ oop/goops/stklos.scm \
+ oop/goops/accessors.scm \
+ oop/goops/simple.scm \
+ \
+ scripts/compile.scm \
+ scripts/disassemble.scm \
+ scripts/display-commentary.scm \
+ scripts/doc-snarf.scm \
+ scripts/frisk.scm \
+ scripts/generate-autoload.scm \
+ scripts/help.scm \
+ scripts/lint.scm \
+ scripts/list.scm \
+ scripts/punify.scm \
+ scripts/read-scheme-source.scm \
+ scripts/read-text-outline.scm \
+ scripts/use2dot.scm \
+ scripts/snarf-check-and-output-texi.scm \
+ scripts/summarize-guile-TODO.scm \
+ scripts/api-diff.scm \
+ scripts/read-rfc822.scm \
+ scripts/snarf-guile-m4-docs.scm \
+ scripts/autofrisk.scm \
+ scripts/scan-api.scm \
+ \
+ srfi/srfi-1.scm \
+ srfi/srfi-2.scm \
+ srfi/srfi-4.scm \
+ srfi/srfi-4/gnu.scm \
+ srfi/srfi-6.scm \
+ srfi/srfi-8.scm \
+ srfi/srfi-9.scm \
+ srfi/srfi-9/gnu.scm \
+ srfi/srfi-10.scm \
+ srfi/srfi-11.scm \
+ srfi/srfi-13.scm \
+ srfi/srfi-14.scm \
+ srfi/srfi-16.scm \
+ srfi/srfi-17.scm \
+ srfi/srfi-18.scm \
+ srfi/srfi-19.scm \
+ srfi/srfi-26.scm \
+ srfi/srfi-27.scm \
+ srfi/srfi-28.scm \
+ srfi/srfi-31.scm \
+ srfi/srfi-34.scm \
+ srfi/srfi-35.scm \
+ srfi/srfi-37.scm \
+ srfi/srfi-38.scm \
+ srfi/srfi-41.scm \
+ srfi/srfi-42.scm \
+ srfi/srfi-43.scm \
+ srfi/srfi-39.scm \
+ srfi/srfi-45.scm \
+ srfi/srfi-60.scm \
+ srfi/srfi-64.scm \
+ srfi/srfi-67.scm \
+ srfi/srfi-69.scm \
+ srfi/srfi-88.scm \
+ srfi/srfi-98.scm \
+ srfi/srfi-111.scm \
+ \
+ statprof.scm \
+ \
+ system/base/pmatch.scm \
+ system/base/syntax.scm \
+ system/base/compile.scm \
+ system/base/language.scm \
+ system/base/lalr.scm \
+ system/base/message.scm \
+ system/base/target.scm \
+ system/base/types.scm \
+ system/base/ck.scm \
+ \
system/foreign.scm \
+ \
system/foreign-object.scm \
- system/xref.scm \
+ \
system/repl/debug.scm \
system/repl/error-handling.scm \
system/repl/common.scm \
system/repl/command.scm \
system/repl/repl.scm \
system/repl/server.scm \
- system/repl/coop-server.scm
-
-LIB_SOURCES = \
- statprof.scm \
+ system/repl/coop-server.scm \
+ \
+ system/vm/assembler.scm \
+ system/vm/coverage.scm \
+ system/vm/debug.scm \
+ system/vm/disassembler.scm \
+ system/vm/dwarf.scm \
+ system/vm/elf.scm \
+ system/vm/frame.scm \
+ system/vm/inspect.scm \
+ system/vm/linker.scm \
+ system/vm/loader.scm \
+ system/vm/program.scm \
+ system/vm/trace.scm \
+ system/vm/trap-state.scm \
+ system/vm/traps.scm \
+ system/vm/vm.scm \
+ \
+ system/xref.scm \
+ \
sxml/apply-templates.scm \
sxml/fold.scm \
sxml/match.scm \
@@ -402,6 +339,7 @@ LIB_SOURCES = \
sxml/ssax.scm \
sxml/transform.scm \
sxml/xpath.scm \
+ \
texinfo.scm \
texinfo/docbook.scm \
texinfo/html.scm \
@@ -409,9 +347,8 @@ LIB_SOURCES = \
texinfo/string-utils.scm \
texinfo/plain-text.scm \
texinfo/reflection.scm \
- texinfo/serialize.scm
-
-WEB_SOURCES = \
+ texinfo/serialize.scm \
+ \
web/client.scm \
web/http.scm \
web/request.scm \
@@ -420,10 +357,8 @@ WEB_SOURCES = \
web/server/http.scm \
web/uri.scm
-EXTRA_DIST += oop/ChangeLog-2008
-
ELISP_SOURCES = \
- language/elisp/boot.el
+ language/elisp/boot.el
NOCOMP_SOURCES = \
ice-9/match.upstream.scm \
@@ -441,3 +376,21 @@ NOCOMP_SOURCES = \
sxml/upstream/SXPath-old.scm \
sxml/upstream/assert.scm \
sxml/upstream/input-parse.scm
+
+## ice-9/test.scm is not currently installed.
+EXTRA_DIST += \
+ ice-9/test.scm \
+ ice-9/compile-psyntax.scm \
+ ice-9/ChangeLog-2008 \
+ scripts/ChangeLog-2008 \
+ scripts/README \
+ oop/ChangeLog-2008
+
+ETAGS_ARGS += \
+ ice-9/test.scm \
+ ice-9/compile-psyntax.scm
+
+ice-9/psyntax-pp.scm.gen:
+ $(top_builddir_absolute)/meta/guile --no-auto-compile -s $(srcdir)/ice-9/compile-psyntax.scm \
+ $(srcdir)/ice-9/psyntax.scm $(srcdir)/ice-9/psyntax-pp.scm
+.PHONY: ice-9/psyntax-pp.scm.gen
From 1c9e23c0901363ff24c1dc4e085e7ed68cb81e6d Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Fri, 23 Oct 2015 16:38:49 +0200
Subject: [PATCH 070/865] Bootstrap makefile tweaks
* bootstrap/Makefile.am (.scm.go): Fix -L options to point to /module.
(SOURCES): Move up psyntax-pp.scm. Sadly a bug while I was developing
the bootstrap makefile meant that there was a stale psyntax-pp.go from
module/ that the build was picking up, leading to invalid
bootstrap timing measurements.
---
bootstrap/Makefile.am | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/bootstrap/Makefile.am b/bootstrap/Makefile.am
index 4f0bfac40..441b72738 100644
--- a/bootstrap/Makefile.am
+++ b/bootstrap/Makefile.am
@@ -27,7 +27,12 @@ GUILE_OPTIMIZATIONS = -O1
nobase_noinst_DATA = $(GOBJECTS) ice-9/eval.go
CLEANFILES = $(GOBJECTS) ice-9/eval.go
-$(GOBJECTS): ice-9/eval.go
+# We must build the evaluator first, so that we can be sure to control
+# the stack. Then we really need to build the expander before other
+# things, otherwise the compile time for everything else is dominated by
+# the expander.
+ice-9/psyntax-pp.go: ice-9/eval.go
+$(GOBJECTS): ice-9/psyntax-pp.go
VM_TARGETS := system/vm/assembler.go system/vm/disassembler.go
$(VM_TARGETS): $(top_builddir)/libguile/vm-operations.h
@@ -45,7 +50,7 @@ SUFFIXES = .scm .go
$(top_builddir)/meta/uninstalled-env \
guild compile --target="$(host)" \
$(GUILE_WARNINGS) $(GUILE_OPTIMIZATIONS) \
- -L "$(abs_srcdir)" -L "$(abs_builddir)" \
+ -L "$(abs_top_srcdir)/module" \
-L "$(abs_top_srcdir)/guile-readline" \
-o "$@" "$<"
@@ -120,7 +125,6 @@ SOURCES = \
system/base/types.scm \
system/base/ck.scm \
\
- ice-9/psyntax-pp.scm \
ice-9/boot-9.scm \
ice-9/r5rs.scm \
ice-9/deprecated.scm \
From 95ac2204d9084a01677fff9eb2669c495bd07d3d Mon Sep 17 00:00:00 2001
From: Andy Wingo
Date: Sun, 25 Oct 2015 10:49:41 +0000
Subject: [PATCH 071/865] Revert "Update Gnulib to v0.1-603-g1d16a7b"
This reverts commit 2d4da30fdefbcdb065d4b1f48f2a77d06f69e3c3.
This Gnulib update was causing failures related to timezones in
stime.c. I tried to fix it by adopting the time_rz module from gnulib
but that then caused other failures. We can try again later.
---
GNUmakefile | 2 +-
build-aux/announce-gen | 24 +-
build-aux/config.rpath | 18 +-
build-aux/gendocs.sh | 115 ++-
build-aux/git-version-gen | 11 +-
build-aux/gitlog-to-changelog | 176 ++---
build-aux/gnu-web-doc-update | 19 +-
build-aux/gnupload | 2 +-
build-aux/snippet/arg-nonnull.h | 2 +-
build-aux/snippet/c++defs.h | 2 +-
build-aux/snippet/unused-parameter.h | 2 +-
build-aux/snippet/warn-on-use.h | 2 +-
build-aux/useless-if-before-free | 2 +-
build-aux/vc-list-files | 2 +-
doc/gendocs_template | 20 +-
gnulib-local/build-aux/git-version-gen.diff | 14 +-
lib/Makefile.am | 75 +-
lib/accept.c | 2 +-
lib/alignof.h | 2 +-
lib/alloca.in.h | 2 +-
lib/arpa_inet.in.h | 2 +-
lib/asnprintf.c | 2 +-
lib/assure.h | 37 -
lib/basename-lgpl.c | 2 +-
lib/binary-io.c | 1 -
lib/binary-io.h | 2 +-
lib/bind.c | 2 +-
lib/btowc.c | 2 +-
lib/byteswap.in.h | 2 +-
lib/c-ctype.c | 394 ++++++++++-
lib/c-ctype.h | 453 +++++-------
lib/c-strcase.h | 2 +-
lib/c-strcasecmp.c | 2 +-
lib/c-strcaseeq.h | 5 +-
lib/c-strncasecmp.c | 2 +-
lib/canonicalize-lgpl.c | 2 +-
lib/ceil.c | 2 +-
lib/close.c | 2 +-
lib/config.charset | 6 +-
lib/connect.c | 2 +-
lib/copysign.c | 2 +-
lib/dirent.in.h | 4 +-
lib/dirfd.c | 2 +-
lib/dirname-lgpl.c | 2 +-
lib/dirname.h | 10 +-
lib/dosname.h | 2 +-
lib/dup2.c | 2 +-
lib/duplocale.c | 2 +-
lib/errno.in.h | 2 +-
lib/fcntl.in.h | 22 +-
lib/fd-hook.c | 2 +-
lib/fd-hook.h | 2 +-
lib/float+.h | 2 +-
lib/float.c | 2 +-
lib/float.in.h | 2 +-
lib/flock.c | 2 +-
lib/floor.c | 2 +-
lib/frexp.c | 2 +-
lib/fstat.c | 2 +-
lib/fsync.c | 2 +-
lib/full-read.c | 2 +-
lib/full-read.h | 3 +-
lib/full-write.c | 2 +-
lib/full-write.h | 2 +-
lib/gai_strerror.c | 2 +-
lib/getaddrinfo.c | 2 +-
lib/getlogin.c | 2 +-
lib/getpeername.c | 2 +-
lib/getsockname.c | 2 +-
lib/getsockopt.c | 2 +-
lib/gettext.h | 2 +-
lib/gettimeofday.c | 2 +-
lib/glthread/lock.h | 2 +-
lib/iconv.c | 2 +-
lib/iconv.in.h | 2 +-
lib/iconv_close.c | 2 +-
lib/iconv_open-aix.h | 2 +-
lib/iconv_open-hpux.h | 4 +-
lib/iconv_open-irix.h | 4 +-
lib/iconv_open-osf.h | 4 +-
lib/iconv_open.c | 2 +-
lib/iconveh.h | 2 +-
lib/ignore-value.h | 37 +
lib/inet_ntop.c | 2 +-
lib/inet_pton.c | 2 +-
lib/isfinite.c | 2 +-
lib/isinf.c | 2 +-
lib/isnan.c | 2 +-
lib/isnand-nolibm.h | 2 +-
lib/isnand.c | 2 +-
lib/isnanf-nolibm.h | 2 +-
lib/isnanf.c | 2 +-
lib/isnanl-nolibm.h | 2 +-
lib/isnanl.c | 2 +-
lib/itold.c | 2 +-
lib/langinfo.in.h | 20 +-
lib/link.c | 2 +-
lib/listen.c | 2 +-
lib/localcharset.c | 81 +--
lib/localcharset.h | 2 +-
lib/locale.in.h | 2 +-
lib/localeconv.c | 2 +-
lib/log.c | 2 +-
lib/log1p.c | 2 +-
lib/lstat.c | 2 +-
lib/malloc.c | 2 +-
lib/malloca.c | 2 +-
lib/malloca.h | 2 +-
lib/math.c | 1 -
lib/math.in.h | 2 +-
lib/mbrtowc.c | 9 +-
lib/mbsinit.c | 2 +-
lib/mbtowc-impl.h | 2 +-
lib/mbtowc.c | 2 +-
lib/memchr.c | 2 +-
lib/mkdir.c | 2 +-
lib/mkstemp.c | 2 +-
lib/mktime-internal.h | 4 -
lib/mktime.c | 741 --------------------
lib/msvc-inval.c | 2 +-
lib/msvc-inval.h | 2 +-
lib/msvc-nothrow.c | 2 +-
lib/msvc-nothrow.h | 2 +-
lib/netdb.in.h | 2 +-
lib/netinet_in.in.h | 2 +-
lib/nl_langinfo.c | 255 +++----
lib/nproc.c | 2 +-
lib/nproc.h | 2 +-
lib/open.c | 2 +-
lib/pathmax.h | 2 +-
lib/pipe.c | 2 +-
lib/pipe2.c | 2 +-
lib/poll.c | 83 ++-
lib/poll.in.h | 2 +-
lib/printf-args.c | 2 +-
lib/printf-args.h | 2 +-
lib/printf-parse.c | 2 +-
lib/printf-parse.h | 2 +-
lib/putenv.c | 2 +-
lib/raise.c | 2 +-
lib/read.c | 2 +-
lib/readlink.c | 2 +-
lib/recv.c | 2 +-
lib/recvfrom.c | 2 +-
lib/ref-add.sin | 2 +-
lib/ref-del.sin | 2 +-
lib/regcomp.c | 44 +-
lib/regex.c | 2 +-
lib/regex.h | 19 +-
lib/regex_internal.c | 23 +-
lib/regex_internal.h | 35 +-
lib/regexec.c | 10 +-
lib/rename.c | 8 +-
lib/rmdir.c | 2 +-
lib/round.c | 2 +-
lib/safe-read.c | 2 +-
lib/safe-read.h | 2 +-
lib/safe-write.c | 2 +-
lib/safe-write.h | 2 +-
lib/same-inode.h | 2 +-
lib/secure_getenv.c | 2 +-
lib/select.c | 38 +-
lib/send.c | 2 +-
lib/sendto.c | 2 +-
lib/setenv.c | 2 +-
lib/setsockopt.c | 2 +-
lib/shutdown.c | 2 +-
lib/signal.in.h | 10 +-
lib/signbitd.c | 2 +-
lib/signbitf.c | 2 +-
lib/signbitl.c | 2 +-
lib/size_max.h | 2 +-
lib/snprintf.c | 2 +-
lib/socket.c | 2 +-
lib/sockets.c | 9 +-
lib/sockets.h | 12 +-
lib/stat-time.h | 18 +-
lib/stat.c | 2 +-
lib/stdalign.in.h | 21 +-
lib/stdbool.in.h | 2 +-
lib/stddef.in.h | 54 +-
lib/stdint.in.h | 5 +-
lib/stdio.in.h | 12 +-
lib/stdlib.in.h | 25 +-
lib/strdup.c | 2 +-
lib/streq.h | 2 +-
lib/strftime.c | 122 ++--
lib/strftime.h | 9 +-
lib/striconveh.c | 2 +-
lib/striconveh.h | 2 +-
lib/string.in.h | 23 +-
lib/stripslash.c | 2 +-
lib/sys_file.in.h | 2 +-
lib/sys_select.in.h | 17 +-
lib/sys_socket.c | 1 -
lib/sys_socket.in.h | 2 +-
lib/sys_stat.in.h | 2 +-
lib/sys_time.in.h | 2 +-
lib/sys_times.in.h | 2 +-
lib/sys_types.in.h | 4 +-
lib/sys_uio.in.h | 2 +-
lib/tempname.c | 120 ++--
lib/tempname.h | 17 +-
lib/time-internal.h | 49 --
lib/time.in.h | 31 +-
lib/time_r.c | 2 +-
lib/time_rz.c | 323 ---------
lib/timegm.c | 38 -
lib/times.c | 4 +-
lib/trunc.c | 2 +-
lib/unistd.c | 1 -
lib/unistd.in.h | 39 +-
lib/unistr.in.h | 2 +-
lib/unistr/u8-mbtouc-aux.c | 2 +-
lib/unistr/u8-mbtouc-unsafe-aux.c | 2 +-
lib/unistr/u8-mbtouc-unsafe.c | 2 +-
lib/unistr/u8-mbtouc.c | 2 +-
lib/unistr/u8-mbtoucr.c | 2 +-
lib/unistr/u8-prev.c | 2 +-
lib/unistr/u8-uctomb-aux.c | 2 +-
lib/unistr/u8-uctomb.c | 2 +-
lib/unitypes.in.h | 2 +-
lib/unsetenv.c | 127 ----
lib/vasnprintf.c | 199 +++---
lib/vasnprintf.h | 2 +-
lib/verify.h | 2 +-
lib/vsnprintf.c | 2 +-
lib/w32sock.h | 2 +-
lib/wchar.in.h | 11 +-
lib/wcrtomb.c | 2 +-
lib/wctype.in.h | 2 +-
lib/write.c | 2 +-
lib/xsize.h | 2 +-
m4/00gnulib.m4 | 2 +-
m4/absolute-header.m4 | 2 +-
m4/alloca.m4 | 2 +-
m4/arpa_inet_h.m4 | 2 +-
m4/autobuild.m4 | 2 +-
m4/btowc.m4 | 2 +-
m4/byteswap.m4 | 2 +-
m4/canonicalize.m4 | 2 +-
m4/ceil.m4 | 2 +-
m4/check-math-lib.m4 | 2 +-
m4/clock_time.m4 | 2 +-
m4/close.m4 | 2 +-
m4/configmake.m4 | 2 +-
m4/copysign.m4 | 2 +-
m4/dirent_h.m4 | 2 +-
m4/dirfd.m4 | 2 +-
m4/dirname.m4 | 2 +-
m4/double-slash-root.m4 | 2 +-
m4/dup2.m4 | 84 +--
m4/duplocale.m4 | 2 +-
m4/eealloc.m4 | 2 +-
m4/environ.m4 | 2 +-
m4/errno_h.m4 | 2 +-
m4/exponentd.m4 | 2 +-
m4/exponentf.m4 | 2 +-
m4/exponentl.m4 | 2 +-
m4/extensions.m4 | 7 +-
m4/extern-inline.m4 | 45 +-
m4/fcntl-o.m4 | 2 +-
m4/fcntl_h.m4 | 2 +-
m4/flexmember.m4 | 41 --
m4/float_h.m4 | 2 +-
m4/flock.m4 | 2 +-
m4/floor.m4 | 2 +-
m4/fpieee.m4 | 4 +-
m4/frexp.m4 | 2 +-
m4/fstat.m4 | 2 +-
m4/fsync.m4 | 2 +-
m4/func.m4 | 2 +-
m4/getaddrinfo.m4 | 2 +-
m4/getlogin.m4 | 2 +-
m4/gettimeofday.m4 | 2 +-
m4/glibc21.m4 | 2 +-
m4/gnulib-cache.m4 | 5 +-
m4/gnulib-common.m4 | 105 ++-
m4/gnulib-comp.m4 | 65 +-
m4/gnulib-tool.m4 | 2 +-
m4/hostent.m4 | 2 +-
m4/iconv.m4 | 61 +-
m4/iconv_h.m4 | 2 +-
m4/iconv_open-utf.m4 | 2 +-
m4/iconv_open.m4 | 2 +-
m4/include_next.m4 | 2 +-
m4/inet_ntop.m4 | 2 +-
m4/inet_pton.m4 | 2 +-
m4/inline.m4 | 2 +-
m4/intmax_t.m4 | 2 +-
m4/inttypes_h.m4 | 2 +-
m4/isfinite.m4 | 40 +-
m4/isinf.m4 | 43 +-
m4/isnan.m4 | 2 +-
m4/isnand.m4 | 2 +-
m4/isnanf.m4 | 2 +-
m4/isnanl.m4 | 43 +-
m4/langinfo_h.m4 | 2 +-
m4/largefile.m4 | 2 +-
m4/ld-version-script.m4 | 47 +-
m4/ldexp.m4 | 2 +-
m4/lib-ld.m4 | 2 +-
m4/lib-link.m4 | 2 +-
m4/lib-prefix.m4 | 2 +-
m4/libunistring-base.m4 | 2 +-
m4/libunistring.m4 | 2 +-
m4/link.m4 | 2 +-
m4/localcharset.m4 | 2 +-
m4/locale-fr.m4 | 2 +-
m4/locale-ja.m4 | 2 +-
m4/locale-zh.m4 | 2 +-
m4/locale_h.m4 | 2 +-
m4/localeconv.m4 | 2 +-
m4/log.m4 | 2 +-
m4/log1p.m4 | 2 +-
m4/longlong.m4 | 2 +-
m4/lstat.m4 | 50 +-
m4/malloc.m4 | 2 +-
m4/malloca.m4 | 2 +-
m4/math_h.m4 | 2 +-
m4/mathfunc.m4 | 2 +-
m4/mbrtowc.m4 | 48 +-
m4/mbsinit.m4 | 2 +-
m4/mbstate_t.m4 | 2 +-
m4/mbtowc.m4 | 2 +-
m4/memchr.m4 | 2 +-
m4/mkdir.m4 | 2 +-
m4/mkstemp.m4 | 2 +-
m4/mktime.m4 | 253 -------
m4/mmap-anon.m4 | 2 +-
m4/mode_t.m4 | 2 +-
m4/msvc-inval.m4 | 2 +-
m4/msvc-nothrow.m4 | 2 +-
m4/multiarch.m4 | 2 +-
m4/netdb_h.m4 | 2 +-
m4/netinet_in_h.m4 | 2 +-
m4/nl_langinfo.m4 | 2 +-
m4/nocrash.m4 | 2 +-
m4/nproc.m4 | 2 +-
m4/off_t.m4 | 2 +-
m4/open.m4 | 2 +-
m4/pathmax.m4 | 2 +-
m4/pipe.m4 | 2 +-
m4/pipe2.m4 | 2 +-
m4/poll.m4 | 2 +-
m4/poll_h.m4 | 2 +-
m4/printf.m4 | 67 +-
m4/putenv.m4 | 2 +-
m4/raise.m4 | 2 +-
m4/read.m4 | 2 +-
m4/readlink.m4 | 2 +-
m4/regex.m4 | 2 +-
m4/rename.m4 | 2 +-
m4/rmdir.m4 | 2 +-
m4/round.m4 | 2 +-
m4/safe-read.m4 | 2 +-
m4/safe-write.m4 | 2 +-
m4/secure_getenv.m4 | 2 +-
m4/select.m4 | 2 +-
m4/servent.m4 | 2 +-
m4/setenv.m4 | 2 +-
m4/signal_h.m4 | 2 +-
m4/signbit.m4 | 2 +-
m4/size_max.m4 | 2 +-
m4/snprintf.m4 | 2 +-
m4/socketlib.m4 | 2 +-
m4/sockets.m4 | 2 +-
m4/socklen.m4 | 2 +-
m4/sockpfaf.m4 | 2 +-
m4/ssize_t.m4 | 2 +-
m4/stat-time.m4 | 2 +-
m4/stat.m4 | 2 +-
m4/stdalign.m4 | 10 +-
m4/stdbool.m4 | 2 +-
m4/stddef_h.m4 | 10 +-
m4/stdint.m4 | 2 +-
m4/stdint_h.m4 | 2 +-
m4/stdio_h.m4 | 31 +-
m4/stdlib_h.m4 | 4 +-
m4/strdup.m4 | 2 +-
m4/strftime.m4 | 2 +-
m4/string_h.m4 | 2 +-
m4/sys_file_h.m4 | 2 +-
m4/sys_select_h.m4 | 2 +-
m4/sys_socket_h.m4 | 2 +-
m4/sys_stat_h.m4 | 2 +-
m4/sys_time_h.m4 | 3 +-
m4/sys_times_h.m4 | 2 +-
m4/sys_types_h.m4 | 2 +-
m4/sys_uio_h.m4 | 2 +-
m4/tempname.m4 | 2 +-
m4/time_h.m4 | 22 +-
m4/time_r.m4 | 2 +-
m4/time_rz.m4 | 21 -
m4/timegm.m4 | 26 -
m4/times.m4 | 2 +-
m4/tm_gmtoff.m4 | 2 +-
m4/trunc.m4 | 2 +-
m4/unistd_h.m4 | 6 +-
m4/vasnprintf.m4 | 2 +-
m4/visibility.m4 | 2 +-
m4/vsnprintf.m4 | 2 +-
m4/warn-on-use.m4 | 2 +-
m4/warnings.m4 | 2 +-
m4/wchar_h.m4 | 2 +-
m4/wchar_t.m4 | 2 +-
m4/wcrtomb.m4 | 2 +-
m4/wctype_h.m4 | 2 +-
m4/wint_t.m4 | 2 +-
m4/write.m4 | 2 +-
m4/xsize.m4 | 2 +-
maint.mk | 48 +-
412 files changed, 1998 insertions(+), 3959 deletions(-)
delete mode 100644 lib/assure.h
create mode 100644 lib/ignore-value.h
delete mode 100644 lib/mktime-internal.h
delete mode 100644 lib/mktime.c
delete mode 100644 lib/time-internal.h
delete mode 100644 lib/time_rz.c
delete mode 100644 lib/timegm.c
delete mode 100644 lib/unsetenv.c
delete mode 100644 m4/flexmember.m4
delete mode 100644 m4/mktime.m4
delete mode 100644 m4/time_rz.m4
delete mode 100644 m4/timegm.m4
diff --git a/GNUmakefile b/GNUmakefile
index 6617eec2e..4ab642943 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -5,7 +5,7 @@
# It is necessary if you want to build targets usually of interest
# only to the maintainer.
-# Copyright (C) 2001, 2003, 2006-2015 Free Software Foundation, Inc.
+# Copyright (C) 2001, 2003, 2006-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/announce-gen b/build-aux/announce-gen
index 8a6edb5d4..db9ed50a7 100755
--- a/build-aux/announce-gen
+++ b/build-aux/announce-gen
@@ -3,13 +3,13 @@ eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
if 0;
# Generate a release announcement message.
-my $VERSION = '2013-07-09 06:39'; # UTC
+my $VERSION = '2012-06-08 06:53'; # UTC
# The definition above must lie within the first 8 lines in order
# for the Emacs time-stamp write hook (at end) to update it.
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2002-2015 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -29,18 +29,15 @@ my $VERSION = '2013-07-09 06:39'; # UTC
use strict;
use Getopt::Long;
+use Digest::MD5;
+eval { require Digest::SHA; }
+ or eval 'use Digest::SHA1';
use POSIX qw(strftime);
(my $ME = $0) =~ s|.*/||;
my %valid_release_types = map {$_ => 1} qw (alpha beta stable);
my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz');
-my %digest_classes =
- (
- 'md5' => (eval { require Digest::MD5; } and 'Digest::MD5'),
- 'sha1' => ((eval { require Digest::SHA; } and 'Digest::SHA')
- or (eval { require Digest::SHA1; } and 'Digest::SHA1'))
- );
my $srcdir = '.';
sub usage ($)
@@ -160,13 +157,15 @@ sub print_checksums (@)
foreach my $meth (qw (md5 sha1))
{
- my $class = $digest_classes{$meth} or next;
foreach my $f (@file)
{
open IN, '<', $f
or die "$ME: $f: cannot open for reading: $!\n";
binmode IN;
- my $dig = $class->new->addfile(*IN)->hexdigest;
+ my $dig =
+ ($meth eq 'md5'
+ ? Digest::MD5->new->addfile(*IN)->hexdigest
+ : Digest::SHA1->new->addfile(*IN)->hexdigest);
close IN;
print "$dig $f\n";
}
@@ -417,15 +416,14 @@ sub get_tool_versions ($$)
@url_dir_list
or (warn "URL directory name(s) not specified\n"), $fail = 1;
- my @tool_list = split ',', $bootstrap_tools
- if $bootstrap_tools;
+ my @tool_list = split ',', $bootstrap_tools;
grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version
and (warn "when specifying gnulib as a tool, you must also specify\n"
. "--gnulib-version=V, where V is the result of running git describe\n"
. "in the gnulib source directory.\n"), $fail = 1;
- !$release_type || exists $valid_release_types{$release_type}
+ exists $valid_release_types{$release_type}
or (warn "'$release_type': invalid release type\n"), $fail = 1;
@ARGV
diff --git a/build-aux/config.rpath b/build-aux/config.rpath
index a3e25c844..ab6fd995f 100755
--- a/build-aux/config.rpath
+++ b/build-aux/config.rpath
@@ -2,7 +2,7 @@
# Output a system dependent set of variables, describing how to set the
# run time search path of shared libraries in an executable.
#
-# Copyright 1996-2015 Free Software Foundation, Inc.
+# Copyright 1996-2014 Free Software Foundation, Inc.
# Taken from GNU libtool, 2001
# Originally by Gordon Matzigkeit , 1996
#
@@ -367,7 +367,11 @@ else
dgux*)
hardcode_libdir_flag_spec='-L$libdir'
;;
- freebsd2.[01]*)
+ freebsd2.2*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ ;;
+ freebsd2*)
hardcode_direct=yes
hardcode_minus_L=yes
;;
@@ -544,11 +548,13 @@ case "$host_os" in
dgux*)
library_names_spec='$libname$shrext'
;;
- freebsd[23].*)
- library_names_spec='$libname$shrext$versuffix'
- ;;
freebsd* | dragonfly*)
- library_names_spec='$libname$shrext'
+ case "$host_os" in
+ freebsd[123]*)
+ library_names_spec='$libname$shrext$versuffix' ;;
+ *)
+ library_names_spec='$libname$shrext' ;;
+ esac
;;
gnu*)
library_names_spec='$libname$shrext'
diff --git a/build-aux/gendocs.sh b/build-aux/gendocs.sh
index c8ca1bbc4..f9ec9df76 100755
--- a/build-aux/gendocs.sh
+++ b/build-aux/gendocs.sh
@@ -2,9 +2,10 @@
# gendocs.sh -- generate a GNU manual in many formats. This script is
# mentioned in maintain.texi. See the help message below for usage details.
-scriptversion=2015-05-05.16
+scriptversion=2013-10-10.09
-# Copyright 2003-2015 Free Software Foundation, Inc.
+# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
+# Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -20,16 +21,17 @@ scriptversion=2015-05-05.16
# along with this program. If not, see .
#
# Original author: Mohit Agarwal.
-# Send bug reports and any other correspondence to bug-gnulib@gnu.org.
+# Send bug reports and any other correspondence to bug-texinfo@gnu.org.
#
# The latest version of this script, and the companion template, is
-# available from the Gnulib repository:
+# available from Texinfo CVS:
+# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs.sh
+# http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs_template
#
-# http://git.savannah.gnu.org/cgit/gnulib.git/tree/build-aux/gendocs.sh
-# http://git.savannah.gnu.org/cgit/gnulib.git/tree/doc/gendocs_template
+# An up-to-date copy is also maintained in Gnulib (gnu.org/software/gnulib).
# TODO:
-# - image importing was only implemented for HTML generated by
+# - image importation was only implemented for HTML generated by
# makeinfo. But it should be simple enough to adjust.
# - images are not imported in the source tarball. All the needed
# formats (PDF, PNG, etc.) should be included.
@@ -37,12 +39,12 @@ scriptversion=2015-05-05.16
prog=`basename "$0"`
srcdir=`pwd`
-scripturl="http://git.savannah.gnu.org/cgit/gnulib.git/plain/build-aux/gendocs.sh"
-templateurl="http://git.savannah.gnu.org/cgit/gnulib.git/plain/doc/gendocs_template"
+scripturl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs.sh"
+templateurl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs_template"
: ${SETLANG="env LANG= LC_MESSAGES= LC_ALL= LANGUAGE="}
: ${MAKEINFO="makeinfo"}
-: ${TEXI2DVI="texi2dvi"}
+: ${TEXI2DVI="texi2dvi -t @finalout"}
: ${DOCBOOK2HTML="docbook2html"}
: ${DOCBOOK2PDF="docbook2pdf"}
: ${DOCBOOK2TXT="docbook2txt"}
@@ -54,7 +56,7 @@ unset use_texi2html
version="gendocs.sh $scriptversion
-Copyright 2015 Free Software Foundation, Inc.
+Copyright 2013 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
@@ -73,16 +75,11 @@ Options:
-o OUTDIR write files into OUTDIR, instead of manual/.
-I DIR append DIR to the Texinfo search path.
--common ARG pass ARG in all invocations.
- --html ARG pass ARG to makeinfo or texi2html for HTML targets,
- instead of --css-ref=/software/gnulib/manual.css.
+ --html ARG pass ARG to makeinfo or texi2html for HTML targets.
--info ARG pass ARG to makeinfo for Info, instead of --no-split.
--no-ascii skip generating the plain text output.
- --no-html skip generating the html output.
- --no-info skip generating the info output.
- --no-tex skip generating the dvi and pdf output.
--source ARG include ARG in tar archive of sources.
--split HOW make split HTML by node, section, chapter; default node.
- --tex ARG pass ARG to texi2dvi for DVI and PDF, instead of -t @finalout.
--texi2html use texi2html to make HTML target, with all split versions.
--docbook convert through DocBook too (xml, txt, html, pdf).
@@ -134,7 +131,7 @@ locale, since that's the language of most Texinfo manuals. If you
happen to have a non-English manual and non-English web site, see the
SETLANG setting in the source.
-Email bug reports or enhancement requests to bug-gnulib@gnu.org.
+Email bug reports or enhancement requests to bug-texinfo@gnu.org.
"
MANUAL_TITLE=
@@ -142,18 +139,14 @@ PACKAGE=
EMAIL=webmasters@gnu.org # please override with --email
commonarg= # passed to all makeinfo/texi2html invcations.
dirargs= # passed to all tools (-I dir).
-dirs= # -I directories.
-htmlarg=--css-ref=/software/gnulib/manual.css
+dirs= # -I's directories.
+htmlarg=
infoarg=--no-split
generate_ascii=true
-generate_html=true
-generate_info=true
-generate_tex=true
outdir=manual
source_extra=
split=node
srcfile=
-texarg="-t @finalout"
while test $# -gt 0; do
case $1 in
@@ -166,12 +159,8 @@ while test $# -gt 0; do
--html) shift; htmlarg=$1;;
--info) shift; infoarg=$1;;
--no-ascii) generate_ascii=false;;
- --no-html) generate_ascii=false;;
- --no-info) generate_info=false;;
- --no-tex) generate_tex=false;;
--source) shift; source_extra=$1;;
--split) shift; split=$1;;
- --tex) shift; texarg=$1;;
--texi2html) use_texi2html=1;;
--help) echo "$usage"; exit 0;;
@@ -232,9 +221,8 @@ calcsize()
# copy_images OUTDIR HTML-FILE...
# -------------------------------
-# Copy all the images needed by the HTML-FILEs into OUTDIR.
-# Look for them in . and the -I directories; this is simpler than what
-# makeinfo supports with -I, but hopefully it will suffice.
+# Copy all the images needed by the HTML-FILEs into OUTDIR. Look
+# for them in the -I directories.
copy_images()
{
local odir
@@ -244,7 +232,7 @@ copy_images()
BEGIN {
\$me = '$prog';
\$odir = '$odir';
- @dirs = qw(. $dirs);
+ @dirs = qw($dirs);
}
" -e '
/
${srcdir}/$PACKAGE-db.xml"
@@ -457,8 +431,7 @@ if test -n "$docbook"; then
mv $PACKAGE-db.pdf "$outdir/"
fi
-#
-printf "\nMaking index.html for $PACKAGE...\n"
+printf "\nMaking index file...\n"
if test -z "$use_texi2html"; then
CONDS="/%%IF *HTML_SECTION%%/,/%%ENDIF *HTML_SECTION%%/d;\
/%%IF *HTML_CHAPTER%%/,/%%ENDIF *HTML_CHAPTER%%/d"
diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen
index 8e185a9d1..1e5d556e9 100755
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
@@ -1,8 +1,8 @@
#!/bin/sh
# Print a version string.
-scriptversion=2014-12-02.19; # UTC
+scriptversion=2012-12-31.23; # UTC
-# Copyright (C) 2007-2015 Free Software Foundation, Inc.
+# Copyright (C) 2007-2014 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -85,10 +85,9 @@ Print a version string.
Options:
- --prefix PREFIX prefix of git tags (default 'v')
+ --prefix prefix of git tags (default 'v')
--match pattern for git tags to match (default: '\$prefix*')
- --fallback VERSION
- fallback version to use if \"git --version\" fails
+ --fallback fallback version to use if \"git --version\" fails
--help display this help and exit
--version output version information and exit
@@ -221,7 +220,7 @@ if test "x$v_from_git" != x; then
fi
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
-printf %s "$v"
+echo "$v" | tr -d "$nl"
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog
index a0e0a05df..78afff4e8 100755
--- a/build-aux/gitlog-to-changelog
+++ b/build-aux/gitlog-to-changelog
@@ -3,13 +3,13 @@ eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
if 0;
# Convert git log output to ChangeLog format.
-my $VERSION = '2015-06-11 01:03'; # UTC
+my $VERSION = '2012-07-29 06:11'; # UTC
# The definition above must lie within the first 8 lines in order
# for the Emacs time-stamp write hook (at end) to update it.
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2008-2015 Free Software Foundation, Inc.
+# Copyright (C) 2008-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -72,9 +72,6 @@ OPTIONS:
directory can be derived.
--since=DATE convert only the logs since DATE;
the default is to convert all log entries.
- --until=DATE convert only the logs older than DATE.
- --ignore-matching=PAT ignore commit messages whose first lines match PAT.
- --ignore-line=PAT ignore lines of commit messages that match PAT.
--format=FMT set format string for commit subject and body;
see 'man git-log' for the list of format metacharacters;
the default is '%s%n%b%n'
@@ -223,13 +220,10 @@ sub git_dir_option($)
{
my $since_date;
- my $until_date;
my $format_string = '%s%n%b%n';
my $amend_file;
my $append_dot = 0;
my $cluster = 1;
- my $ignore_matching;
- my $ignore_line;
my $strip_tab = 0;
my $strip_cherry_pick = 0;
my $srcdir;
@@ -238,13 +232,10 @@ sub git_dir_option($)
help => sub { usage 0 },
version => sub { print "$ME version $VERSION\n"; exit },
'since=s' => \$since_date,
- 'until=s' => \$until_date,
'format=s' => \$format_string,
'amend=s' => \$amend_file,
'append-dot' => \$append_dot,
'cluster!' => \$cluster,
- 'ignore-matching=s' => \$ignore_matching,
- 'ignore-line=s' => \$ignore_line,
'strip-tab' => \$strip_tab,
'strip-cherry-pick' => \$strip_cherry_pick,
'srcdir=s' => \$srcdir,
@@ -252,8 +243,6 @@ sub git_dir_option($)
defined $since_date
and unshift @ARGV, "--since=$since_date";
- defined $until_date
- and unshift @ARGV, "--until=$until_date";
# This is a hash that maps an SHA1 to perl code (i.e., s/old/new/)
# that makes a correction in the log or attribution of that commit.
@@ -270,7 +259,6 @@ sub git_dir_option($)
my $prev_multi_paragraph;
my $prev_date_line = '';
my @prev_coauthors = ();
- my @skipshas = ();
while (1)
{
defined (my $in = )
@@ -291,21 +279,6 @@ sub git_dir_option($)
$sha =~ /^[0-9a-fA-F]{40}$/
or die "$ME:$.: invalid SHA1: $sha\n";
- my $skipflag = 0;
- if (@skipshas)
- {
- foreach(@skipshas)
- {
- if ($sha =~ /^$_/)
- {
- $skipflag = 1;
- ## Perhaps only warn if a pattern matches more than once?
- warn "$ME: warning: skipping $sha due to $_\n";
- last;
- }
- }
- }
-
# If this commit's log requires any transformation, do it now.
my $code = $amend_code->{$sha};
if (defined $code)
@@ -333,7 +306,7 @@ sub git_dir_option($)
$rest =~ s/^\s*\(cherry picked from commit [\da-f]+\)\n//m;
}
- my @line = split /[ \t]*\n/, $rest;
+ my @line = split "\n", $rest;
my $author_line = shift @line;
defined $author_line
or die "$ME:$.: unexpected EOF\n";
@@ -343,18 +316,17 @@ sub git_dir_option($)
# Format 'Copyright-paperwork-exempt: Yes' as a standard ChangeLog
# `(tiny change)' annotation.
- my $tiny = (grep (/^(?:Copyright-paperwork-exempt|Tiny-change):\s+[Yy]es$/, @line)
+ my $tiny = (grep (/^Copyright-paperwork-exempt:\s+[Yy]es$/, @line)
? ' (tiny change)' : '');
my $date_line = sprintf "%s %s$tiny\n",
- strftime ("%Y-%m-%d", localtime ($1)), $2;
+ strftime ("%F", localtime ($1)), $2;
my @coauthors = grep /^Co-authored-by:.*$/, @line;
# Omit meta-data lines we've already interpreted.
@line = grep !/^(?:Signed-off-by:[ ].*>$
|Co-authored-by:[ ]
|Copyright-paperwork-exempt:[ ]
- |Tiny-change:[ ]
)/x, @line;
# Remove leading and trailing blank lines.
@@ -364,100 +336,68 @@ sub git_dir_option($)
while ($line[$#line] =~ /^\s*$/) { pop @line; }
}
- # Handle Emacs gitmerge.el "skipped" commits.
- # Yes, this should be controlled by an option. So sue me.
- if ( grep /^(; )?Merge from /, @line )
- {
- my $found = 0;
- foreach (@line)
- {
- if (grep /^The following commit.*skipped:$/, $_)
- {
- $found = 1;
- ## Reset at each merge to reduce chance of false matches.
- @skipshas = ();
- next;
- }
- if ($found && $_ =~ /^([0-9a-fA-F]{7,}) [^ ]/)
- {
- push ( @skipshas, $1 );
- }
- }
- }
+ # Record whether there are two or more paragraphs.
+ my $multi_paragraph = grep /^\s*$/, @line;
- # Ignore commits that match the --ignore-matching pattern, if specified.
- if (! ($skipflag || (defined $ignore_matching
- && @line && $line[0] =~ /$ignore_matching/)))
+ # Format 'Co-authored-by: A U Thor ' lines in
+ # standard multi-author ChangeLog format.
+ for (@coauthors)
{
- if (defined $ignore_line && @line)
- {
- @line = grep ! /$ignore_line/, @line;
- while ($line[$#line] =~ /^\s*$/) { pop @line; }
- }
+ s/^Co-authored-by:\s*/\t /;
+ s/\s* ;
- # Record whether there are two or more paragraphs.
- my $multi_paragraph = grep /^\s*$/, @line;
+ /<.*?@.*\..*>/
+ or warn "$ME: warning: missing email address for "
+ . substr ($_, 5) . "\n";
+ }
- # Format 'Co-authored-by: A U Thor ' lines in
- # standard multi-author ChangeLog format.
- for (@coauthors)
- {
- s/^Co-authored-by:\s*/\t /;
- s/\s* ;
+ # If clustering of commit messages has been disabled, if this header
+ # would be different from the previous date/name/email/coauthors header,
+ # or if this or the previous entry consists of two or more paragraphs,
+ # then print the header.
+ if ( ! $cluster
+ || $date_line ne $prev_date_line
+ || "@coauthors" ne "@prev_coauthors"
+ || $multi_paragraph
+ || $prev_multi_paragraph)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ @coauthors
+ and print join ("\n", @coauthors), "\n";
+ }
+ $prev_date_line = $date_line;
+ @prev_coauthors = @coauthors;
+ $prev_multi_paragraph = $multi_paragraph;
- /<.*?@.*\..*>/
- or warn "$ME: warning: missing email address for "
- . substr ($_, 5) . "\n";
- }
-
- # If clustering of commit messages has been disabled, if this header
- # would be different from the previous date/name/etc. header,
- # or if this or the previous entry consists of two or more paragraphs,
- # then print the header.
- if ( ! $cluster
- || $date_line ne $prev_date_line
- || "@coauthors" ne "@prev_coauthors"
- || $multi_paragraph
- || $prev_multi_paragraph)
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n $date_line\n";
+ }
+ else
+ {
+ if ($append_dot)
{
- $prev_date_line eq ''
- or print "\n";
- print $date_line;
- @coauthors
- and print join ("\n", @coauthors), "\n";
- }
- $prev_date_line = $date_line;
- @prev_coauthors = @coauthors;
- $prev_multi_paragraph = $multi_paragraph;
-
- # If there were any lines
- if (@line == 0)
- {
- warn "$ME: warning: empty commit message:\n $date_line\n";
- }
- else
- {
- if ($append_dot)
+ # If the first line of the message has enough room, then
+ if (length $line[0] < 72)
{
- # If the first line of the message has enough room, then
- if (length $line[0] < 72)
- {
- # append a dot if there is no other punctuation or blank
- # at the end.
- $line[0] =~ /[[:punct:]\s]$/
- or $line[0] .= '.';
- }
+ # append a dot if there is no other punctuation or blank
+ # at the end.
+ $line[0] =~ /[[:punct:]\s]$/
+ or $line[0] .= '.';
}
-
- # Remove one additional leading TAB from each line.
- $strip_tab
- and map { s/^\t// } @line;
-
- # Prefix each non-empty line with a TAB.
- @line = map { length $_ ? "\t$_" : '' } @line;
-
- print "\n", join ("\n", @line), "\n";
}
+
+ # Remove one additional leading TAB from each line.
+ $strip_tab
+ and map { s/^\t// } @line;
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
}
defined ($in = )
diff --git a/build-aux/gnu-web-doc-update b/build-aux/gnu-web-doc-update
index c8fc7e5de..7af2f185f 100755
--- a/build-aux/gnu-web-doc-update
+++ b/build-aux/gnu-web-doc-update
@@ -2,9 +2,9 @@
# Run this after each non-alpha release, to update the web documentation at
# http://www.gnu.org/software/$pkg/manual/
-VERSION=2015-06-16.06; # UTC
+VERSION=2012-12-16.14; # UTC
-# Copyright (C) 2009-2015 Free Software Foundation, Inc.
+# Copyright (C) 2009-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -40,7 +40,6 @@ assumes all documentation is in the doc/ sub-directory.
Options:
-C, --builddir=DIR location of (configured) Makefile (default: .)
-n, --dry-run don't actually commit anything
- -m, --mirror remove out of date files from document server
--help print this help, then exit
--version print version number, then exit
@@ -108,7 +107,6 @@ find_tool XARGS gxargs xargs
builddir=.
dryrun=
-rm_stale='echo'
while test $# != 0
do
# Handle --option=value by splitting apart and putting back on argv.
@@ -125,7 +123,6 @@ do
--help|--version) ${1#--};;
-C|--builddir) shift; builddir=$1; shift ;;
-n|--dry-run) dryrun=echo; shift;;
- -m|--mirror) rm_stale=''; shift;;
--*) die "unrecognized option: $1";;
*) break;;
esac
@@ -162,7 +159,6 @@ $GIT submodule update --recursive
./bootstrap
srcdir=$(pwd)
cd "$builddir"
-builddir=$(pwd)
./config.status --recheck
./config.status
make
@@ -186,17 +182,6 @@ $RSYNC -avP "$builddir"/doc/manual/ $tmp/$pkg/manual
find . -name CVS -prune -o -print \
| $XARGS --no-run-if-empty -- $dryrun $CVS add -ko
- # Report/Remove stale files
- # excluding doc server specific files like CVS/* and .symlinks
- if test -n "$rm_stale"; then
- echo 'Consider the --mirror option if all of the manual is generated,' >&2
- echo 'which will run `cvs remove` to remove stale files.' >&2
- fi
- { find . \( -name CVS -o -type f -name '.*' \) -prune -o -type f -print
- (cd "$builddir"/doc/manual/ && find . -type f -print | sed p)
- } | sort | uniq -u \
- | $XARGS --no-run-if-empty -- ${rm_stale:-$dryrun} $CVS remove -f
-
$dryrun $CVS ci -m $version
)
diff --git a/build-aux/gnupload b/build-aux/gnupload
index f87c195dc..2da97d894 100755
--- a/build-aux/gnupload
+++ b/build-aux/gnupload
@@ -3,7 +3,7 @@
scriptversion=2013-03-19.17; # UTC
-# Copyright (C) 2004-2015 Free Software Foundation, Inc.
+# Copyright (C) 2004-2014 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/snippet/arg-nonnull.h b/build-aux/snippet/arg-nonnull.h
index 0d55e2bce..9ee8b1555 100644
--- a/build-aux/snippet/arg-nonnull.h
+++ b/build-aux/snippet/arg-nonnull.h
@@ -1,5 +1,5 @@
/* A C macro for declaring that specific arguments must not be NULL.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/c++defs.h b/build-aux/snippet/c++defs.h
index 585b38ab3..67b12335d 100644
--- a/build-aux/snippet/c++defs.h
+++ b/build-aux/snippet/c++defs.h
@@ -1,5 +1,5 @@
/* C++ compatible function declaration macros.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/unused-parameter.h b/build-aux/snippet/unused-parameter.h
index f507eb745..41d9510ca 100644
--- a/build-aux/snippet/unused-parameter.h
+++ b/build-aux/snippet/unused-parameter.h
@@ -1,5 +1,5 @@
/* A C macro for declaring that specific function parameters are not used.
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/snippet/warn-on-use.h b/build-aux/snippet/warn-on-use.h
index 90f4985ce..1c4d7bd4e 100644
--- a/build-aux/snippet/warn-on-use.h
+++ b/build-aux/snippet/warn-on-use.h
@@ -1,5 +1,5 @@
/* A C macro for emitting warnings if a function is used.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
diff --git a/build-aux/useless-if-before-free b/build-aux/useless-if-before-free
index 82a09b39e..4c76c75d7 100755
--- a/build-aux/useless-if-before-free
+++ b/build-aux/useless-if-before-free
@@ -10,7 +10,7 @@ my $VERSION = '2012-01-06 07:23'; # UTC
# If you change this file with Emacs, please let the write hook
# do its job. Otherwise, update this string manually.
-# Copyright (C) 2008-2015 Free Software Foundation, Inc.
+# Copyright (C) 2008-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/build-aux/vc-list-files b/build-aux/vc-list-files
index 3bf93c3c0..b2bca54c9 100755
--- a/build-aux/vc-list-files
+++ b/build-aux/vc-list-files
@@ -4,7 +4,7 @@
# Print a version string.
scriptversion=2011-05-16.22; # UTC
-# Copyright (C) 2006-2015 Free Software Foundation, Inc.
+# Copyright (C) 2006-2014 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/doc/gendocs_template b/doc/gendocs_template
index df7faa3d3..4836df787 100644
--- a/doc/gendocs_template
+++ b/doc/gendocs_template
@@ -1,6 +1,5 @@
-
-%%TITLE%% - GNU Project - Free Software Foundation
+%%TITLE%% - GNU Project - Free Software Foundation (FSF)
%%TITLE%%
@@ -68,22 +67,19 @@ script.)
diff --git a/gnulib-local/build-aux/git-version-gen.diff b/gnulib-local/build-aux/git-version-gen.diff
index e15acf336..f875f49d9 100644
--- a/gnulib-local/build-aux/git-version-gen.diff
+++ b/gnulib-local/build-aux/git-version-gen.diff
@@ -4,15 +4,15 @@ Remove when integrated in Gnulib.
--- a/build-aux/git-version-gen
+++ b/build-aux/git-version-gen
-@@ -86,6 +86,7 @@
+@@ -86,6 +86,7 @@ Print a version string.
Options:
- --prefix PREFIX prefix of git tags (default 'v')
+ --prefix prefix of git tags (default 'v')
+ --match pattern for git tags to match (default: '\$prefix*')
- --fallback VERSION
- fallback version to use if \"git --version\" fails
+ --fallback fallback version to use if \"git --version\" fails
-@@ -97,11 +98,15 @@
+ --help display this help and exit
+@@ -96,11 +97,15 @@ Running without arguments will suffice in most cases."
prefix=v
fallback=
@@ -28,7 +28,7 @@ Remove when integrated in Gnulib.
--fallback) shift; fallback="$1";;
-*)
echo "$0: Unknown option '$1'." >&2
-@@ -125,6 +130,7 @@
+@@ -124,6 +129,7 @@ if test "x$tarball_version_file" = x; then
exit 1
fi
@@ -36,7 +36,7 @@ Remove when integrated in Gnulib.
tag_sed_script="${tag_sed_script:-s/x/x/}"
nl='
-@@ -155,7 +161,7 @@
+@@ -154,7 +160,7 @@ then
# directory, and "git describe" output looks sensible, use that to
# derive a version string.
elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e5a4aed8b..5d9c902fc 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,6 +1,6 @@
## DO NOT EDIT! GENERATED AUTOMATICALLY!
## Process this file with automake to produce Makefile.in.
-# Copyright (C) 2002-2015 Free Software Foundation, Inc.
+# Copyright (C) 2002-2014 Free Software Foundation, Inc.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -176,13 +176,6 @@ EXTRA_DIST += arpa_inet.in.h
## end gnulib module arpa_inet
-## begin gnulib module assure
-
-
-EXTRA_DIST += assure.h
-
-## end gnulib module assure
-
## begin gnulib module binary-io
libgnu_la_SOURCES += binary-io.h binary-io.c
@@ -706,9 +699,6 @@ EXTRA_DIST += $(top_srcdir)/build-aux/gnupload
## begin gnulib module gperf
GPERF = gperf
-V_GPERF = $(V_GPERF_@AM_V@)
-V_GPERF_ = $(V_GPERF_@AM_DEFAULT_V@)
-V_GPERF_0 = @echo " GPERF " $@;
## end gnulib module gperf
@@ -758,19 +748,19 @@ EXTRA_DIST += iconv.in.h
## begin gnulib module iconv_open
iconv_open-aix.h: iconv_open-aix.gperf
- $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-aix.gperf > $(srcdir)/iconv_open-aix.h-t && \
+ $(GPERF) -m 10 $(srcdir)/iconv_open-aix.gperf > $(srcdir)/iconv_open-aix.h-t
mv $(srcdir)/iconv_open-aix.h-t $(srcdir)/iconv_open-aix.h
iconv_open-hpux.h: iconv_open-hpux.gperf
- $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-hpux.gperf > $(srcdir)/iconv_open-hpux.h-t && \
+ $(GPERF) -m 10 $(srcdir)/iconv_open-hpux.gperf > $(srcdir)/iconv_open-hpux.h-t
mv $(srcdir)/iconv_open-hpux.h-t $(srcdir)/iconv_open-hpux.h
iconv_open-irix.h: iconv_open-irix.gperf
- $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-irix.gperf > $(srcdir)/iconv_open-irix.h-t && \
+ $(GPERF) -m 10 $(srcdir)/iconv_open-irix.gperf > $(srcdir)/iconv_open-irix.h-t
mv $(srcdir)/iconv_open-irix.h-t $(srcdir)/iconv_open-irix.h
iconv_open-osf.h: iconv_open-osf.gperf
- $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-osf.gperf > $(srcdir)/iconv_open-osf.h-t && \
+ $(GPERF) -m 10 $(srcdir)/iconv_open-osf.gperf > $(srcdir)/iconv_open-osf.h-t
mv $(srcdir)/iconv_open-osf.h-t $(srcdir)/iconv_open-osf.h
iconv_open-solaris.h: iconv_open-solaris.gperf
- $(V_GPERF)$(GPERF) -m 10 $(srcdir)/iconv_open-solaris.gperf > $(srcdir)/iconv_open-solaris.h-t && \
+ $(GPERF) -m 10 $(srcdir)/iconv_open-solaris.gperf > $(srcdir)/iconv_open-solaris.h-t
mv $(srcdir)/iconv_open-solaris.h-t $(srcdir)/iconv_open-solaris.h
BUILT_SOURCES += iconv_open-aix.h iconv_open-hpux.h iconv_open-irix.h iconv_open-osf.h iconv_open-solaris.h
MOSTLYCLEANFILES += iconv_open-aix.h-t iconv_open-hpux.h-t iconv_open-irix.h-t iconv_open-osf.h-t iconv_open-solaris.h-t
@@ -1456,24 +1446,6 @@ EXTRA_libgnu_la_SOURCES += mkstemp.c
## end gnulib module mkstemp
-## begin gnulib module mktime
-
-
-EXTRA_DIST += mktime-internal.h mktime.c
-
-EXTRA_libgnu_la_SOURCES += mktime.c
-
-## end gnulib module mktime
-
-## begin gnulib module mktime-internal
-
-
-EXTRA_DIST += mktime-internal.h mktime.c
-
-EXTRA_libgnu_la_SOURCES += mktime.c
-
-## end gnulib module mktime-internal
-
## begin gnulib module msvc-inval
@@ -2088,7 +2060,6 @@ stddef.h: stddef.in.h $(top_builddir)/config.status
-e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
-e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
-e 's|@''NEXT_STDDEF_H''@|$(NEXT_STDDEF_H)|g' \
- -e 's|@''HAVE_MAX_ALIGN_T''@|$(HAVE_MAX_ALIGN_T)|g' \
-e 's|@''HAVE_WCHAR_T''@|$(HAVE_WCHAR_T)|g' \
-e 's|@''REPLACE_NULL''@|$(REPLACE_NULL)|g' \
< $(srcdir)/stddef.in.h; \
@@ -2315,7 +2286,6 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-e 's/@''GNULIB_PTSNAME''@/$(GNULIB_PTSNAME)/g' \
-e 's/@''GNULIB_PTSNAME_R''@/$(GNULIB_PTSNAME_R)/g' \
-e 's/@''GNULIB_PUTENV''@/$(GNULIB_PUTENV)/g' \
- -e 's/@''GNULIB_QSORT_R''@/$(GNULIB_QSORT_R)/g' \
-e 's/@''GNULIB_RANDOM''@/$(GNULIB_RANDOM)/g' \
-e 's/@''GNULIB_RANDOM_R''@/$(GNULIB_RANDOM_R)/g' \
-e 's/@''GNULIB_REALLOC_POSIX''@/$(GNULIB_REALLOC_POSIX)/g' \
@@ -2367,7 +2337,6 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-e 's|@''REPLACE_PTSNAME''@|$(REPLACE_PTSNAME)|g' \
-e 's|@''REPLACE_PTSNAME_R''@|$(REPLACE_PTSNAME_R)|g' \
-e 's|@''REPLACE_PUTENV''@|$(REPLACE_PUTENV)|g' \
- -e 's|@''REPLACE_QSORT_R''@|$(REPLACE_QSORT_R)|g' \
-e 's|@''REPLACE_RANDOM_R''@|$(REPLACE_RANDOM_R)|g' \
-e 's|@''REPLACE_REALLOC''@|$(REPLACE_REALLOC)|g' \
-e 's|@''REPLACE_REALPATH''@|$(REPLACE_REALPATH)|g' \
@@ -2843,12 +2812,10 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(
-e 's/@''GNULIB_STRPTIME''@/$(GNULIB_STRPTIME)/g' \
-e 's/@''GNULIB_TIMEGM''@/$(GNULIB_TIMEGM)/g' \
-e 's/@''GNULIB_TIME_R''@/$(GNULIB_TIME_R)/g' \
- -e 's/@''GNULIB_TIME_RZ''@/$(GNULIB_TIME_RZ)/g' \
-e 's|@''HAVE_DECL_LOCALTIME_R''@|$(HAVE_DECL_LOCALTIME_R)|g' \
-e 's|@''HAVE_NANOSLEEP''@|$(HAVE_NANOSLEEP)|g' \
-e 's|@''HAVE_STRPTIME''@|$(HAVE_STRPTIME)|g' \
-e 's|@''HAVE_TIMEGM''@|$(HAVE_TIMEGM)|g' \
- -e 's|@''HAVE_TIMEZONE_T''@|$(HAVE_TIMEZONE_T)|g' \
-e 's|@''REPLACE_GMTIME''@|$(REPLACE_GMTIME)|g' \
-e 's|@''REPLACE_LOCALTIME''@|$(REPLACE_LOCALTIME)|g' \
-e 's|@''REPLACE_LOCALTIME_R''@|$(REPLACE_LOCALTIME_R)|g' \
@@ -2858,7 +2825,6 @@ time.h: time.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(
-e 's|@''PTHREAD_H_DEFINES_STRUCT_TIMESPEC''@|$(PTHREAD_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e 's|@''SYS_TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(SYS_TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e 's|@''TIME_H_DEFINES_STRUCT_TIMESPEC''@|$(TIME_H_DEFINES_STRUCT_TIMESPEC)|g' \
- -e 's|@''UNISTD_H_DEFINES_STRUCT_TIMESPEC''@|$(UNISTD_H_DEFINES_STRUCT_TIMESPEC)|g' \
-e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
-e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
-e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
@@ -2880,24 +2846,6 @@ EXTRA_libgnu_la_SOURCES += time_r.c
## end gnulib module time_r
-## begin gnulib module time_rz
-
-
-EXTRA_DIST += time-internal.h time_rz.c
-
-EXTRA_libgnu_la_SOURCES += time_rz.c
-
-## end gnulib module time_rz
-
-## begin gnulib module timegm
-
-
-EXTRA_DIST += mktime-internal.h timegm.c
-
-EXTRA_libgnu_la_SOURCES += timegm.c
-
-## end gnulib module timegm
-
## begin gnulib module times
@@ -3047,11 +2995,9 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H
-e 's|@''REPLACE_PWRITE''@|$(REPLACE_PWRITE)|g' \
-e 's|@''REPLACE_READ''@|$(REPLACE_READ)|g' \
-e 's|@''REPLACE_READLINK''@|$(REPLACE_READLINK)|g' \
- -e 's|@''REPLACE_READLINKAT''@|$(REPLACE_READLINKAT)|g' \
-e 's|@''REPLACE_RMDIR''@|$(REPLACE_RMDIR)|g' \
-e 's|@''REPLACE_SLEEP''@|$(REPLACE_SLEEP)|g' \
-e 's|@''REPLACE_SYMLINK''@|$(REPLACE_SYMLINK)|g' \
- -e 's|@''REPLACE_SYMLINKAT''@|$(REPLACE_SYMLINKAT)|g' \
-e 's|@''REPLACE_TTYNAME_R''@|$(REPLACE_TTYNAME_R)|g' \
-e 's|@''REPLACE_UNLINK''@|$(REPLACE_UNLINK)|g' \
-e 's|@''REPLACE_UNLINKAT''@|$(REPLACE_UNLINKAT)|g' \
@@ -3142,15 +3088,6 @@ EXTRA_DIST += unitypes.in.h
## end gnulib module unitypes
-## begin gnulib module unsetenv
-
-
-EXTRA_DIST += unsetenv.c
-
-EXTRA_libgnu_la_SOURCES += unsetenv.c
-
-## end gnulib module unsetenv
-
## begin gnulib module useless-if-before-free
diff --git a/lib/accept.c b/lib/accept.c
index 441c065db..b216c6bd6 100644
--- a/lib/accept.c
+++ b/lib/accept.c
@@ -1,6 +1,6 @@
/* accept.c --- wrappers for Windows accept function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/alignof.h b/lib/alignof.h
index 713f3bd89..280f3e384 100644
--- a/lib/alignof.h
+++ b/lib/alignof.h
@@ -1,5 +1,5 @@
/* Determine alignment of types.
- Copyright (C) 2003-2004, 2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2003-2004, 2006, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/alloca.in.h b/lib/alloca.in.h
index dc1b55060..e3aa62d2d 100644
--- a/lib/alloca.in.h
+++ b/lib/alloca.in.h
@@ -1,6 +1,6 @@
/* Memory allocation on the stack.
- Copyright (C) 1995, 1999, 2001-2004, 2006-2015 Free Software Foundation,
+ Copyright (C) 1995, 1999, 2001-2004, 2006-2014 Free Software Foundation,
Inc.
This program is free software; you can redistribute it and/or modify it
diff --git a/lib/arpa_inet.in.h b/lib/arpa_inet.in.h
index 6ad528cc6..3f5df4776 100644
--- a/lib/arpa_inet.in.h
+++ b/lib/arpa_inet.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2005-2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2008-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/asnprintf.c b/lib/asnprintf.c
index 413b8d763..7806f6888 100644
--- a/lib/asnprintf.c
+++ b/lib/asnprintf.c
@@ -1,5 +1,5 @@
/* Formatted output to strings.
- Copyright (C) 1999, 2002, 2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2002, 2006, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/assure.h b/lib/assure.h
deleted file mode 100644
index 867688e01..000000000
--- a/lib/assure.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Run-time assert-like macros.
-
- Copyright (C) 2014-2015 Free Software Foundation, Inc.
-
- This program 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 program 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 program. If not, see . */
-
-/* Written by Paul Eggert. */
-
-#ifndef _GL_ASSURE_H
-#define _GL_ASSURE_H
-
-#include
-
-/* Check E's value at runtime, and report an error and abort if not.
- However, do nothng if NDEBUG is defined.
-
- Unlike standard 'assert', this macro always compiles E even when NDEBUG
- is defined, so as to catch typos and avoid some GCC warnings. */
-
-#ifdef NDEBUG
-# define assure(E) ((void) (0 && (E)))
-#else
-# define assure(E) assert (E)
-#endif
-
-#endif
diff --git a/lib/basename-lgpl.c b/lib/basename-lgpl.c
index 12d1e522b..fe007936f 100644
--- a/lib/basename-lgpl.c
+++ b/lib/basename-lgpl.c
@@ -1,6 +1,6 @@
/* basename.c -- return the last element in a file name
- Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2015 Free Software
+ Copyright (C) 1990, 1998-2001, 2003-2006, 2009-2014 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/binary-io.c b/lib/binary-io.c
index d828bcd01..8bbdb44d1 100644
--- a/lib/binary-io.c
+++ b/lib/binary-io.c
@@ -1,4 +1,3 @@
#include
#define BINARY_IO_INLINE _GL_EXTERN_INLINE
#include "binary-io.h"
-typedef int dummy;
diff --git a/lib/binary-io.h b/lib/binary-io.h
index 1a87e8f3b..c276faa88 100644
--- a/lib/binary-io.h
+++ b/lib/binary-io.h
@@ -1,5 +1,5 @@
/* Binary mode I/O.
- Copyright (C) 2001, 2003, 2005, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2003, 2005, 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/bind.c b/lib/bind.c
index 72263cc1b..36750c9a8 100644
--- a/lib/bind.c
+++ b/lib/bind.c
@@ -1,6 +1,6 @@
/* bind.c --- wrappers for Windows bind function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/btowc.c b/lib/btowc.c
index 01e80c50a..aad27f593 100644
--- a/lib/btowc.c
+++ b/lib/btowc.c
@@ -1,5 +1,5 @@
/* Convert unibyte character to wide character.
- Copyright (C) 2008, 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008, 2010-2014 Free Software Foundation, Inc.
Written by Bruno Haible , 2008.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/byteswap.in.h b/lib/byteswap.in.h
index 673c53b25..130c79dfb 100644
--- a/lib/byteswap.in.h
+++ b/lib/byteswap.in.h
@@ -1,5 +1,5 @@
/* byteswap.h - Byte swapping
- Copyright (C) 2005, 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2007, 2009-2014 Free Software Foundation, Inc.
Written by Oskar Liljeblad , 2005.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/c-ctype.c b/lib/c-ctype.c
index 5d9d4d87a..7fe3f7efa 100644
--- a/lib/c-ctype.c
+++ b/lib/c-ctype.c
@@ -1,3 +1,395 @@
+/* Character handling in C locale.
+
+ Copyright 2000-2003, 2006, 2009-2014 Free Software Foundation, Inc.
+
+This program 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 2 of the License, or
+(at your option) any later version.
+
+This program 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 program; if not, see . */
+
#include
-#define C_CTYPE_INLINE _GL_EXTERN_INLINE
+
+/* Specification. */
+#define NO_C_CTYPE_MACROS
#include "c-ctype.h"
+
+/* The function isascii is not locale dependent. Its use in EBCDIC is
+ questionable. */
+bool
+c_isascii (int c)
+{
+ return (c >= 0x00 && c <= 0x7f);
+}
+
+bool
+c_isalnum (int c)
+{
+#if C_CTYPE_CONSECUTIVE_DIGITS \
+ && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+ return ((c >= '0' && c <= '9')
+ || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z'));
+#else
+ return ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z'));
+#endif
+#else
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isalpha (int c)
+{
+#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+ return ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z');
+#else
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+#endif
+#else
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isblank (int c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+bool
+c_iscntrl (int c)
+{
+#if C_CTYPE_ASCII
+ return ((c & ~0x1f) == 0 || c == 0x7f);
+#else
+ switch (c)
+ {
+ case ' ': case '!': case '"': case '#': case '$': case '%':
+ case '&': case '\'': case '(': case ')': case '*': case '+':
+ case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>': case '?':
+ case '@':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '[': case '\\': case ']': case '^': case '_': case '`':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '{': case '|': case '}': case '~':
+ return 0;
+ default:
+ return 1;
+ }
+#endif
+}
+
+bool
+c_isdigit (int c)
+{
+#if C_CTYPE_CONSECUTIVE_DIGITS
+ return (c >= '0' && c <= '9');
+#else
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_islower (int c)
+{
+#if C_CTYPE_CONSECUTIVE_LOWERCASE
+ return (c >= 'a' && c <= 'z');
+#else
+ switch (c)
+ {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isgraph (int c)
+{
+#if C_CTYPE_ASCII
+ return (c >= '!' && c <= '~');
+#else
+ switch (c)
+ {
+ case '!': case '"': case '#': case '$': case '%': case '&':
+ case '\'': case '(': case ')': case '*': case '+': case ',':
+ case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>': case '?':
+ case '@':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '[': case '\\': case ']': case '^': case '_': case '`':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '{': case '|': case '}': case '~':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isprint (int c)
+{
+#if C_CTYPE_ASCII
+ return (c >= ' ' && c <= '~');
+#else
+ switch (c)
+ {
+ case ' ': case '!': case '"': case '#': case '$': case '%':
+ case '&': case '\'': case '(': case ')': case '*': case '+':
+ case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ case ':': case ';': case '<': case '=': case '>': case '?':
+ case '@':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '[': case '\\': case ']': case '^': case '_': case '`':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
+ case 's': case 't': case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case '{': case '|': case '}': case '~':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_ispunct (int c)
+{
+#if C_CTYPE_ASCII
+ return ((c >= '!' && c <= '~')
+ && !((c >= '0' && c <= '9')
+ || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'Z')));
+#else
+ switch (c)
+ {
+ case '!': case '"': case '#': case '$': case '%': case '&':
+ case '\'': case '(': case ')': case '*': case '+': case ',':
+ case '-': case '.': case '/':
+ case ':': case ';': case '<': case '=': case '>': case '?':
+ case '@':
+ case '[': case '\\': case ']': case '^': case '_': case '`':
+ case '{': case '|': case '}': case '~':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isspace (int c)
+{
+ return (c == ' ' || c == '\t'
+ || c == '\n' || c == '\v' || c == '\f' || c == '\r');
+}
+
+bool
+c_isupper (int c)
+{
+#if C_CTYPE_CONSECUTIVE_UPPERCASE
+ return (c >= 'A' && c <= 'Z');
+#else
+ switch (c)
+ {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+bool
+c_isxdigit (int c)
+{
+#if C_CTYPE_CONSECUTIVE_DIGITS \
+ && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+ return ((c >= '0' && c <= '9')
+ || ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'F'));
+#else
+ return ((c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || (c >= 'a' && c <= 'f'));
+#endif
+#else
+ switch (c)
+ {
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ return 1;
+ default:
+ return 0;
+ }
+#endif
+}
+
+int
+c_tolower (int c)
+{
+#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+ return (c >= 'A' && c <= 'Z' ? c - 'A' + 'a' : c);
+#else
+ switch (c)
+ {
+ case 'A': return 'a';
+ case 'B': return 'b';
+ case 'C': return 'c';
+ case 'D': return 'd';
+ case 'E': return 'e';
+ case 'F': return 'f';
+ case 'G': return 'g';
+ case 'H': return 'h';
+ case 'I': return 'i';
+ case 'J': return 'j';
+ case 'K': return 'k';
+ case 'L': return 'l';
+ case 'M': return 'm';
+ case 'N': return 'n';
+ case 'O': return 'o';
+ case 'P': return 'p';
+ case 'Q': return 'q';
+ case 'R': return 'r';
+ case 'S': return 's';
+ case 'T': return 't';
+ case 'U': return 'u';
+ case 'V': return 'v';
+ case 'W': return 'w';
+ case 'X': return 'x';
+ case 'Y': return 'y';
+ case 'Z': return 'z';
+ default: return c;
+ }
+#endif
+}
+
+int
+c_toupper (int c)
+{
+#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+ return (c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c);
+#else
+ switch (c)
+ {
+ case 'a': return 'A';
+ case 'b': return 'B';
+ case 'c': return 'C';
+ case 'd': return 'D';
+ case 'e': return 'E';
+ case 'f': return 'F';
+ case 'g': return 'G';
+ case 'h': return 'H';
+ case 'i': return 'I';
+ case 'j': return 'J';
+ case 'k': return 'K';
+ case 'l': return 'L';
+ case 'm': return 'M';
+ case 'n': return 'N';
+ case 'o': return 'O';
+ case 'p': return 'P';
+ case 'q': return 'Q';
+ case 'r': return 'R';
+ case 's': return 'S';
+ case 't': return 'T';
+ case 'u': return 'U';
+ case 'v': return 'V';
+ case 'w': return 'W';
+ case 'x': return 'X';
+ case 'y': return 'Y';
+ case 'z': return 'Z';
+ default: return c;
+ }
+#endif
+}
diff --git a/lib/c-ctype.h b/lib/c-ctype.h
index c7da46f54..a258019f4 100644
--- a/lib/c-ctype.h
+++ b/lib/c-ctype.h
@@ -5,7 +5,7 @@
functions' behaviour depends on the current locale set via
setlocale.
- Copyright (C) 2000-2003, 2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2003, 2006, 2008-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -25,13 +25,6 @@ along with this program; if not, see . */
#include
-#ifndef _GL_INLINE_HEADER_BEGIN
- #error "Please include config.h first."
-#endif
-_GL_INLINE_HEADER_BEGIN
-#ifndef C_CTYPE_INLINE
-# define C_CTYPE_INLINE _GL_INLINE
-#endif
#ifdef __cplusplus
extern "C" {
@@ -46,6 +39,38 @@ extern "C" {
characters. */
+/* Check whether the ASCII optimizations apply. */
+
+/* ANSI C89 (and ISO C99 5.2.1.3 too) already guarantees that
+ '0', '1', ..., '9' have consecutive integer values. */
+#define C_CTYPE_CONSECUTIVE_DIGITS 1
+
+#if ('A' <= 'Z') \
+ && ('A' + 1 == 'B') && ('B' + 1 == 'C') && ('C' + 1 == 'D') \
+ && ('D' + 1 == 'E') && ('E' + 1 == 'F') && ('F' + 1 == 'G') \
+ && ('G' + 1 == 'H') && ('H' + 1 == 'I') && ('I' + 1 == 'J') \
+ && ('J' + 1 == 'K') && ('K' + 1 == 'L') && ('L' + 1 == 'M') \
+ && ('M' + 1 == 'N') && ('N' + 1 == 'O') && ('O' + 1 == 'P') \
+ && ('P' + 1 == 'Q') && ('Q' + 1 == 'R') && ('R' + 1 == 'S') \
+ && ('S' + 1 == 'T') && ('T' + 1 == 'U') && ('U' + 1 == 'V') \
+ && ('V' + 1 == 'W') && ('W' + 1 == 'X') && ('X' + 1 == 'Y') \
+ && ('Y' + 1 == 'Z')
+#define C_CTYPE_CONSECUTIVE_UPPERCASE 1
+#endif
+
+#if ('a' <= 'z') \
+ && ('a' + 1 == 'b') && ('b' + 1 == 'c') && ('c' + 1 == 'd') \
+ && ('d' + 1 == 'e') && ('e' + 1 == 'f') && ('f' + 1 == 'g') \
+ && ('g' + 1 == 'h') && ('h' + 1 == 'i') && ('i' + 1 == 'j') \
+ && ('j' + 1 == 'k') && ('k' + 1 == 'l') && ('l' + 1 == 'm') \
+ && ('m' + 1 == 'n') && ('n' + 1 == 'o') && ('o' + 1 == 'p') \
+ && ('p' + 1 == 'q') && ('q' + 1 == 'r') && ('r' + 1 == 's') \
+ && ('s' + 1 == 't') && ('t' + 1 == 'u') && ('u' + 1 == 'v') \
+ && ('v' + 1 == 'w') && ('w' + 1 == 'x') && ('x' + 1 == 'y') \
+ && ('y' + 1 == 'z')
+#define C_CTYPE_CONSECUTIVE_LOWERCASE 1
+#endif
+
#if (' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
&& (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
@@ -71,84 +96,11 @@ extern "C" {
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)
/* The character set is ASCII or one of its variants or extensions, not EBCDIC.
Testing the value of '\n' and '\r' is not relevant. */
-# define C_CTYPE_ASCII 1
-#elif ! (' ' == '\x40' && '0' == '\xf0' \
- && 'A' == '\xc1' && 'J' == '\xd1' && 'S' == '\xe2' \
- && 'a' == '\x81' && 'j' == '\x91' && 's' == '\xa2')
-# error "Only ASCII and EBCDIC are supported"
+#define C_CTYPE_ASCII 1
#endif
-#if 'A' < 0
-# error "EBCDIC and char is signed -- not supported"
-#endif
-/* Cases for control characters. */
-
-#define _C_CTYPE_CNTRL \
- case '\a': case '\b': case '\f': case '\n': \
- case '\r': case '\t': case '\v': \
- _C_CTYPE_OTHER_CNTRL
-
-/* ASCII control characters other than those with \-letter escapes. */
-
-#if C_CTYPE_ASCII
-# define _C_CTYPE_OTHER_CNTRL \
- case '\x00': case '\x01': case '\x02': case '\x03': \
- case '\x04': case '\x05': case '\x06': case '\x0e': \
- case '\x0f': case '\x10': case '\x11': case '\x12': \
- case '\x13': case '\x14': case '\x15': case '\x16': \
- case '\x17': case '\x18': case '\x19': case '\x1a': \
- case '\x1b': case '\x1c': case '\x1d': case '\x1e': \
- case '\x1f': case '\x7f'
-#else
- /* Use EBCDIC code page 1047's assignments for ASCII control chars;
- assume all EBCDIC code pages agree about these assignments. */
-# define _C_CTYPE_OTHER_CNTRL \
- case '\x00': case '\x01': case '\x02': case '\x03': \
- case '\x07': case '\x0e': case '\x0f': case '\x10': \
- case '\x11': case '\x12': case '\x13': case '\x18': \
- case '\x19': case '\x1c': case '\x1d': case '\x1e': \
- case '\x1f': case '\x26': case '\x27': case '\x2d': \
- case '\x2e': case '\x32': case '\x37': case '\x3c': \
- case '\x3d': case '\x3f'
-#endif
-
-/* Cases for lowercase hex letters, and lowercase letters, all offset by N. */
-
-#define _C_CTYPE_LOWER_A_THRU_F_N(n) \
- case 'a' + (n): case 'b' + (n): case 'c' + (n): case 'd' + (n): \
- case 'e' + (n): case 'f' + (n)
-#define _C_CTYPE_LOWER_N(n) \
- _C_CTYPE_LOWER_A_THRU_F_N(n): \
- case 'g' + (n): case 'h' + (n): case 'i' + (n): case 'j' + (n): \
- case 'k' + (n): case 'l' + (n): case 'm' + (n): case 'n' + (n): \
- case 'o' + (n): case 'p' + (n): case 'q' + (n): case 'r' + (n): \
- case 's' + (n): case 't' + (n): case 'u' + (n): case 'v' + (n): \
- case 'w' + (n): case 'x' + (n): case 'y' + (n): case 'z' + (n)
-
-/* Cases for hex letters, digits, lower, punct, and upper. */
-
-#define _C_CTYPE_A_THRU_F \
- _C_CTYPE_LOWER_A_THRU_F_N (0): \
- _C_CTYPE_LOWER_A_THRU_F_N ('A' - 'a')
-#define _C_CTYPE_DIGIT \
- case '0': case '1': case '2': case '3': \
- case '4': case '5': case '6': case '7': \
- case '8': case '9'
-#define _C_CTYPE_LOWER _C_CTYPE_LOWER_N (0)
-#define _C_CTYPE_PUNCT \
- case '!': case '"': case '#': case '$': \
- case '%': case '&': case '\'': case '(': \
- case ')': case '*': case '+': case ',': \
- case '-': case '.': case '/': case ':': \
- case ';': case '<': case '=': case '>': \
- case '?': case '@': case '[': case '\\': \
- case ']': case '^': case '_': case '`': \
- case '{': case '|': case '}': case '~'
-#define _C_CTYPE_UPPER _C_CTYPE_LOWER_N ('A' - 'a')
-
-
-/* Function definitions. */
+/* Function declarations. */
/* Unlike the functions in , which require an argument in the range
of the 'unsigned char' type, the functions here operate on values that are
@@ -165,202 +117,179 @@ extern "C" {
if (c_isalpha (*s)) ...
*/
-C_CTYPE_INLINE bool
-c_isalnum (int c)
-{
- switch (c)
- {
- _C_CTYPE_DIGIT:
- _C_CTYPE_LOWER:
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+extern bool c_isascii (int c) _GL_ATTRIBUTE_CONST; /* not locale dependent */
-C_CTYPE_INLINE bool
-c_isalpha (int c)
-{
- switch (c)
- {
- _C_CTYPE_LOWER:
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+extern bool c_isalnum (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isalpha (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isblank (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_iscntrl (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isdigit (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_islower (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isgraph (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isprint (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_ispunct (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isspace (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isupper (int c) _GL_ATTRIBUTE_CONST;
+extern bool c_isxdigit (int c) _GL_ATTRIBUTE_CONST;
-/* The function isascii is not locale dependent.
- Its use in EBCDIC is questionable. */
-C_CTYPE_INLINE bool
-c_isascii (int c)
-{
- switch (c)
- {
- case ' ':
- _C_CTYPE_CNTRL:
- _C_CTYPE_DIGIT:
- _C_CTYPE_LOWER:
- _C_CTYPE_PUNCT:
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+extern int c_tolower (int c) _GL_ATTRIBUTE_CONST;
+extern int c_toupper (int c) _GL_ATTRIBUTE_CONST;
-C_CTYPE_INLINE bool
-c_isblank (int c)
-{
- return c == ' ' || c == '\t';
-}
-C_CTYPE_INLINE bool
-c_iscntrl (int c)
-{
- switch (c)
- {
- _C_CTYPE_CNTRL:
- return true;
- default:
- return false;
- }
-}
+#if (defined __GNUC__ && !defined __STRICT_ANSI__ && defined __OPTIMIZE__ \
+ && !defined __OPTIMIZE_SIZE__ && !defined NO_C_CTYPE_MACROS)
-C_CTYPE_INLINE bool
-c_isdigit (int c)
-{
- switch (c)
- {
- _C_CTYPE_DIGIT:
- return true;
- default:
- return false;
- }
-}
+/* ASCII optimizations. */
-C_CTYPE_INLINE bool
-c_isgraph (int c)
-{
- switch (c)
- {
- _C_CTYPE_DIGIT:
- _C_CTYPE_LOWER:
- _C_CTYPE_PUNCT:
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+#undef c_isascii
+#define c_isascii(c) \
+ ({ int __c = (c); \
+ (__c >= 0x00 && __c <= 0x7f); \
+ })
-C_CTYPE_INLINE bool
-c_islower (int c)
-{
- switch (c)
- {
- _C_CTYPE_LOWER:
- return true;
- default:
- return false;
- }
-}
+#if C_CTYPE_CONSECUTIVE_DIGITS \
+ && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+#undef c_isalnum
+#define c_isalnum(c) \
+ ({ int __c = (c); \
+ ((__c >= '0' && __c <= '9') \
+ || ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'Z')); \
+ })
+#else
+#undef c_isalnum
+#define c_isalnum(c) \
+ ({ int __c = (c); \
+ ((__c >= '0' && __c <= '9') \
+ || (__c >= 'A' && __c <= 'Z') \
+ || (__c >= 'a' && __c <= 'z')); \
+ })
+#endif
+#endif
-C_CTYPE_INLINE bool
-c_isprint (int c)
-{
- switch (c)
- {
- case ' ':
- _C_CTYPE_DIGIT:
- _C_CTYPE_LOWER:
- _C_CTYPE_PUNCT:
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+#undef c_isalpha
+#define c_isalpha(c) \
+ ({ int __c = (c); \
+ ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'Z'); \
+ })
+#else
+#undef c_isalpha
+#define c_isalpha(c) \
+ ({ int __c = (c); \
+ ((__c >= 'A' && __c <= 'Z') || (__c >= 'a' && __c <= 'z')); \
+ })
+#endif
+#endif
-C_CTYPE_INLINE bool
-c_ispunct (int c)
-{
- switch (c)
- {
- _C_CTYPE_PUNCT:
- return true;
- default:
- return false;
- }
-}
+#undef c_isblank
+#define c_isblank(c) \
+ ({ int __c = (c); \
+ (__c == ' ' || __c == '\t'); \
+ })
-C_CTYPE_INLINE bool
-c_isspace (int c)
-{
- switch (c)
- {
- case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
- return true;
- default:
- return false;
- }
-}
+#if C_CTYPE_ASCII
+#undef c_iscntrl
+#define c_iscntrl(c) \
+ ({ int __c = (c); \
+ ((__c & ~0x1f) == 0 || __c == 0x7f); \
+ })
+#endif
-C_CTYPE_INLINE bool
-c_isupper (int c)
-{
- switch (c)
- {
- _C_CTYPE_UPPER:
- return true;
- default:
- return false;
- }
-}
+#if C_CTYPE_CONSECUTIVE_DIGITS
+#undef c_isdigit
+#define c_isdigit(c) \
+ ({ int __c = (c); \
+ (__c >= '0' && __c <= '9'); \
+ })
+#endif
-C_CTYPE_INLINE bool
-c_isxdigit (int c)
-{
- switch (c)
- {
- _C_CTYPE_DIGIT:
- _C_CTYPE_A_THRU_F:
- return true;
- default:
- return false;
- }
-}
+#if C_CTYPE_CONSECUTIVE_LOWERCASE
+#undef c_islower
+#define c_islower(c) \
+ ({ int __c = (c); \
+ (__c >= 'a' && __c <= 'z'); \
+ })
+#endif
-C_CTYPE_INLINE int
-c_tolower (int c)
-{
- switch (c)
- {
- _C_CTYPE_UPPER:
- return c - 'A' + 'a';
- default:
- return c;
- }
-}
+#if C_CTYPE_ASCII
+#undef c_isgraph
+#define c_isgraph(c) \
+ ({ int __c = (c); \
+ (__c >= '!' && __c <= '~'); \
+ })
+#endif
+
+#if C_CTYPE_ASCII
+#undef c_isprint
+#define c_isprint(c) \
+ ({ int __c = (c); \
+ (__c >= ' ' && __c <= '~'); \
+ })
+#endif
+
+#if C_CTYPE_ASCII
+#undef c_ispunct
+#define c_ispunct(c) \
+ ({ int _c = (c); \
+ (c_isgraph (_c) && ! c_isalnum (_c)); \
+ })
+#endif
+
+#undef c_isspace
+#define c_isspace(c) \
+ ({ int __c = (c); \
+ (__c == ' ' || __c == '\t' \
+ || __c == '\n' || __c == '\v' || __c == '\f' || __c == '\r'); \
+ })
+
+#if C_CTYPE_CONSECUTIVE_UPPERCASE
+#undef c_isupper
+#define c_isupper(c) \
+ ({ int __c = (c); \
+ (__c >= 'A' && __c <= 'Z'); \
+ })
+#endif
+
+#if C_CTYPE_CONSECUTIVE_DIGITS \
+ && C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#if C_CTYPE_ASCII
+#undef c_isxdigit
+#define c_isxdigit(c) \
+ ({ int __c = (c); \
+ ((__c >= '0' && __c <= '9') \
+ || ((__c & ~0x20) >= 'A' && (__c & ~0x20) <= 'F')); \
+ })
+#else
+#undef c_isxdigit
+#define c_isxdigit(c) \
+ ({ int __c = (c); \
+ ((__c >= '0' && __c <= '9') \
+ || (__c >= 'A' && __c <= 'F') \
+ || (__c >= 'a' && __c <= 'f')); \
+ })
+#endif
+#endif
+
+#if C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+#undef c_tolower
+#define c_tolower(c) \
+ ({ int __c = (c); \
+ (__c >= 'A' && __c <= 'Z' ? __c - 'A' + 'a' : __c); \
+ })
+#undef c_toupper
+#define c_toupper(c) \
+ ({ int __c = (c); \
+ (__c >= 'a' && __c <= 'z' ? __c - 'a' + 'A' : __c); \
+ })
+#endif
+
+#endif /* optimizing for speed */
-C_CTYPE_INLINE int
-c_toupper (int c)
-{
- switch (c)
- {
- _C_CTYPE_LOWER:
- return c - 'a' + 'A';
- default:
- return c;
- }
-}
#ifdef __cplusplus
}
#endif
-_GL_INLINE_HEADER_END
-
#endif /* C_CTYPE_H */
diff --git a/lib/c-strcase.h b/lib/c-strcase.h
index 55fd801ee..ee3bd3f72 100644
--- a/lib/c-strcase.h
+++ b/lib/c-strcase.h
@@ -1,5 +1,5 @@
/* Case-insensitive string comparison functions in C locale.
- Copyright (C) 1995-1996, 2001, 2003, 2005, 2009-2015 Free Software
+ Copyright (C) 1995-1996, 2001, 2003, 2005, 2009-2014 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/c-strcasecmp.c b/lib/c-strcasecmp.c
index ce0223fcb..5059cc659 100644
--- a/lib/c-strcasecmp.c
+++ b/lib/c-strcasecmp.c
@@ -1,5 +1,5 @@
/* c-strcasecmp.c -- case insensitive string comparator in C locale
- Copyright (C) 1998-1999, 2005-2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1998-1999, 2005-2006, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/c-strcaseeq.h b/lib/c-strcaseeq.h
index ce4b82dd1..44d375148 100644
--- a/lib/c-strcaseeq.h
+++ b/lib/c-strcaseeq.h
@@ -1,5 +1,5 @@
/* Optimized case-insensitive string comparison in C locale.
- Copyright (C) 2001-2002, 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2001-2002, 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
@@ -33,6 +33,9 @@
# if C_CTYPE_ASCII
# define CASEEQ(other,upper) \
(c_isupper (upper) ? ((other) & ~0x20) == (upper) : (other) == (upper))
+# elif C_CTYPE_CONSECUTIVE_UPPERCASE && C_CTYPE_CONSECUTIVE_LOWERCASE
+# define CASEEQ(other,upper) \
+ (c_isupper (upper) ? (other) == (upper) || (other) == (upper) - 'A' + 'a' : (other) == (upper))
# else
# define CASEEQ(other,upper) \
(c_toupper (other) == (upper))
diff --git a/lib/c-strncasecmp.c b/lib/c-strncasecmp.c
index 7a7f61df8..614598156 100644
--- a/lib/c-strncasecmp.c
+++ b/lib/c-strncasecmp.c
@@ -1,5 +1,5 @@
/* c-strncasecmp.c -- case insensitive string comparator in C locale
- Copyright (C) 1998-1999, 2005-2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1998-1999, 2005-2006, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/canonicalize-lgpl.c b/lib/canonicalize-lgpl.c
index 624a40eb9..a999c9c84 100644
--- a/lib/canonicalize-lgpl.c
+++ b/lib/canonicalize-lgpl.c
@@ -1,5 +1,5 @@
/* Return the canonical absolute name of a given file.
- Copyright (C) 1996-2015 Free Software Foundation, Inc.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
This file is part of the GNU C Library.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/ceil.c b/lib/ceil.c
index 236d258b1..7e810357b 100644
--- a/lib/ceil.c
+++ b/lib/ceil.c
@@ -1,5 +1,5 @@
/* Round towards positive infinity.
- Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/close.c b/lib/close.c
index c1f112590..9d2e0276a 100644
--- a/lib/close.c
+++ b/lib/close.c
@@ -1,5 +1,5 @@
/* close replacement.
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/config.charset b/lib/config.charset
index 8083c6029..8fe2507d9 100644
--- a/lib/config.charset
+++ b/lib/config.charset
@@ -1,7 +1,7 @@
#! /bin/sh
# Output a system dependent table of character encoding aliases.
#
-# Copyright (C) 2000-2004, 2006-2015 Free Software Foundation, Inc.
+# Copyright (C) 2000-2004, 2006-2014 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
@@ -348,10 +348,12 @@ case "$os" in
#echo "sun_eu_greek ?" # what is this?
echo "UTF-8 UTF-8"
;;
- freebsd*)
+ freebsd* | os2*)
# FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore
# localcharset.c falls back to using the full locale name
# from the environment variables.
+ # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just
+ # reuse FreeBSD's locale data for OS/2.
echo "C ASCII"
echo "US-ASCII ASCII"
for l in la_LN lt_LN; do
diff --git a/lib/connect.c b/lib/connect.c
index bde8a60bc..295fe95d8 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -1,6 +1,6 @@
/* connect.c --- wrappers for Windows connect function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/copysign.c b/lib/copysign.c
index 761320bc0..616ea356e 100644
--- a/lib/copysign.c
+++ b/lib/copysign.c
@@ -1,5 +1,5 @@
/* Copy sign into another 'double' number.
- Copyright (C) 2011-2015 Free Software Foundation, Inc.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dirent.in.h b/lib/dirent.in.h
index e4490caf5..3418bd9dc 100644
--- a/lib/dirent.in.h
+++ b/lib/dirent.in.h
@@ -1,5 +1,5 @@
/* A GNU-like .
- Copyright (C) 2006-2015 Free Software Foundation, Inc.
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -77,7 +77,6 @@ typedef struct gl_directory DIR;
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef opendir
# define opendir rpl_opendir
-# define GNULIB_defined_opendir 1
# endif
_GL_FUNCDECL_RPL (opendir, DIR *, (const char *dir_name) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (opendir, DIR *, (const char *dir_name));
@@ -129,7 +128,6 @@ _GL_WARN_ON_USE (rewinddir, "rewinddir is not portable - "
# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
# undef closedir
# define closedir rpl_closedir
-# define GNULIB_defined_closedir 1
# endif
_GL_FUNCDECL_RPL (closedir, int, (DIR *dirp) _GL_ARG_NONNULL ((1)));
_GL_CXXALIAS_RPL (closedir, int, (DIR *dirp));
diff --git a/lib/dirfd.c b/lib/dirfd.c
index c196c6601..86f8e0a1a 100644
--- a/lib/dirfd.c
+++ b/lib/dirfd.c
@@ -1,6 +1,6 @@
/* dirfd.c -- return the file descriptor associated with an open DIR*
- Copyright (C) 2001, 2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2006, 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dirname-lgpl.c b/lib/dirname-lgpl.c
index c1e610109..121d38754 100644
--- a/lib/dirname-lgpl.c
+++ b/lib/dirname-lgpl.c
@@ -1,6 +1,6 @@
/* dirname.c -- return all but the last element in a file name
- Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2015 Free Software
+ Copyright (C) 1990, 1998, 2000-2001, 2003-2006, 2009-2014 Free Software
Foundation, Inc.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/dirname.h b/lib/dirname.h
index abb058497..e31cb6190 100644
--- a/lib/dirname.h
+++ b/lib/dirname.h
@@ -1,6 +1,6 @@
/* Take file names apart into directory and base names.
- Copyright (C) 1998, 2001, 2003-2006, 2009-2015 Free Software Foundation,
+ Copyright (C) 1998, 2001, 2003-2006, 2009-2014 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
@@ -31,10 +31,6 @@
# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
# endif
-#ifdef __cplusplus
-extern "C" {
-#endif
-
# if GNULIB_DIRNAME
char *base_name (char const *file);
char *dir_name (char const *file);
@@ -47,8 +43,4 @@ char *last_component (char const *file) _GL_ATTRIBUTE_PURE;
bool strip_trailing_slashes (char *file);
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
#endif /* not DIRNAME_H_ */
diff --git a/lib/dosname.h b/lib/dosname.h
index 42e3186ab..b81163d4b 100644
--- a/lib/dosname.h
+++ b/lib/dosname.h
@@ -1,6 +1,6 @@
/* File names on MS-DOS/Windows systems.
- Copyright (C) 2000-2001, 2004-2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2001, 2004-2006, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/dup2.c b/lib/dup2.c
index 7f984ccb0..9709b7a64 100644
--- a/lib/dup2.c
+++ b/lib/dup2.c
@@ -1,6 +1,6 @@
/* Duplicate an open file descriptor to a specified file descriptor.
- Copyright (C) 1999, 2004-2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2004-2007, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/duplocale.c b/lib/duplocale.c
index 9fdb26811..86d5ce59a 100644
--- a/lib/duplocale.c
+++ b/lib/duplocale.c
@@ -1,5 +1,5 @@
/* Duplicate a locale object.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/errno.in.h b/lib/errno.in.h
index 5f1aa8dce..8dbb5f97a 100644
--- a/lib/errno.in.h
+++ b/lib/errno.in.h
@@ -1,6 +1,6 @@
/* A POSIX-like .
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h
index 1c3b2c83f..1cd197002 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -1,6 +1,6 @@
/* Like , but with non-working flags defined to 0.
- Copyright (C) 2006-2015 Free Software Foundation, Inc.
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -34,7 +34,7 @@
extern "C" { ... } block, which leads to errors in C++ mode with the
overridden from gnulib. These errors are known to be gone
with g++ version >= 4.3. */
-#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && (defined __ICC || !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))))
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
# include
#endif
#@INCLUDE_NEXT@ @NEXT_FCNTL_H@
@@ -53,7 +53,7 @@
extern "C" { ... } block, which leads to errors in C++ mode with the
overridden from gnulib. These errors are known to be gone
with g++ version >= 4.3. */
-#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && (defined __ICC || !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))))
+#if !(defined __GLIBC__ || defined __UCLIBC__) || (defined __cplusplus && defined GNULIB_NAMESPACE && !(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
# include
#endif
/* The include_next requires a split double-inclusion guard. */
@@ -186,22 +186,6 @@ _GL_WARN_ON_USE (openat, "openat is not portable - "
/* Fix up the O_* macros. */
-/* AIX 7.1 with XL C 12.1 defines O_CLOEXEC, O_NOFOLLOW, and O_TTY_INIT
- to values outside 'int' range, so omit these misdefinitions.
- But avoid namespace pollution on non-AIX systems. */
-#ifdef _AIX
-# include
-# if defined O_CLOEXEC && ! (INT_MIN <= O_CLOEXEC && O_CLOEXEC <= INT_MAX)
-# undef O_CLOEXEC
-# endif
-# if defined O_NOFOLLOW && ! (INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
-# undef O_NOFOLLOW
-# endif
-# if defined O_TTY_INIT && ! (INT_MIN <= O_TTY_INIT && O_TTY_INIT <= INT_MAX)
-# undef O_TTY_INIT
-# endif
-#endif
-
#if !defined O_DIRECT && defined O_DIRECTIO
/* Tru64 spells it 'O_DIRECTIO'. */
# define O_DIRECT O_DIRECTIO
diff --git a/lib/fd-hook.c b/lib/fd-hook.c
index 158b1ecda..fd07578f1 100644
--- a/lib/fd-hook.c
+++ b/lib/fd-hook.c
@@ -1,5 +1,5 @@
/* Hook for making making file descriptor functions close(), ioctl() extensible.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
Written by Bruno Haible , 2009.
This program is free software: you can redistribute it and/or modify it
diff --git a/lib/fd-hook.h b/lib/fd-hook.h
index 7010ccaef..5ff0f73fc 100644
--- a/lib/fd-hook.h
+++ b/lib/fd-hook.h
@@ -1,5 +1,5 @@
/* Hook for making making file descriptor functions close(), ioctl() extensible.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published
diff --git a/lib/float+.h b/lib/float+.h
index c3d28a598..085c379b1 100644
--- a/lib/float+.h
+++ b/lib/float+.h
@@ -1,5 +1,5 @@
/* Supplemental information about the floating-point formats.
- Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
Written by Bruno Haible , 2007.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/float.c b/lib/float.c
index 9a2d0f723..3faa5eede 100644
--- a/lib/float.c
+++ b/lib/float.c
@@ -1,5 +1,5 @@
/* Auxiliary definitions for .
- Copyright (C) 2011-2015 Free Software Foundation, Inc.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/float.in.h b/lib/float.in.h
index dab2a2b44..e814eaba5 100644
--- a/lib/float.in.h
+++ b/lib/float.in.h
@@ -1,6 +1,6 @@
/* A correct .
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/flock.c b/lib/flock.c
index a591f1e38..928e151b0 100644
--- a/lib/flock.c
+++ b/lib/flock.c
@@ -6,7 +6,7 @@
Written by Richard W.M. Jones
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 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
diff --git a/lib/floor.c b/lib/floor.c
index 010131144..a00f937ed 100644
--- a/lib/floor.c
+++ b/lib/floor.c
@@ -1,5 +1,5 @@
/* Round towards negative infinity.
- Copyright (C) 2007, 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2010-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/frexp.c b/lib/frexp.c
index 21edf0afb..6eff94574 100644
--- a/lib/frexp.c
+++ b/lib/frexp.c
@@ -1,5 +1,5 @@
/* Split a double into fraction and mantissa.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fstat.c b/lib/fstat.c
index 71ca7b0cb..17ccc8e29 100644
--- a/lib/fstat.c
+++ b/lib/fstat.c
@@ -1,5 +1,5 @@
/* fstat() replacement.
- Copyright (C) 2011-2015 Free Software Foundation, Inc.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/fsync.c b/lib/fsync.c
index 8ef6dbee8..99475ff65 100644
--- a/lib/fsync.c
+++ b/lib/fsync.c
@@ -7,7 +7,7 @@
Written by Richard W.M. Jones
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 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
diff --git a/lib/full-read.c b/lib/full-read.c
index ac7fb5651..4d67afb92 100644
--- a/lib/full-read.c
+++ b/lib/full-read.c
@@ -1,5 +1,5 @@
/* An interface to read that retries after partial reads and interrupts.
- Copyright (C) 2002-2003, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/full-read.h b/lib/full-read.h
index 950b9bd3f..954b94dce 100644
--- a/lib/full-read.h
+++ b/lib/full-read.h
@@ -1,6 +1,6 @@
/* An interface to read() that reads all it is asked to read.
- Copyright (C) 2002, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -13,6 +13,7 @@
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, read to the Free Software Foundation,
along with this program. If not, see . */
#include
diff --git a/lib/full-write.c b/lib/full-write.c
index df3bad0d5..6a77b7b45 100644
--- a/lib/full-write.c
+++ b/lib/full-write.c
@@ -1,6 +1,6 @@
/* An interface to read and write that retries (if necessary) until complete.
- Copyright (C) 1993-1994, 1997-2006, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1993-1994, 1997-2006, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/full-write.h b/lib/full-write.h
index 607facd35..2fab6fa02 100644
--- a/lib/full-write.h
+++ b/lib/full-write.h
@@ -1,6 +1,6 @@
/* An interface to write() that writes all it is asked to write.
- Copyright (C) 2002-2003, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2002-2003, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/gai_strerror.c b/lib/gai_strerror.c
index fbef3bbe9..d0c589da1 100644
--- a/lib/gai_strerror.c
+++ b/lib/gai_strerror.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1997, 2001-2002, 2004-2006, 2008-2015 Free Software
+/* Copyright (C) 1997, 2001-2002, 2004-2006, 2008-2014 Free Software
Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Philip Blundell , 1997.
diff --git a/lib/getaddrinfo.c b/lib/getaddrinfo.c
index 9d3384f71..6581dd55a 100644
--- a/lib/getaddrinfo.c
+++ b/lib/getaddrinfo.c
@@ -1,5 +1,5 @@
/* Get address information (partial implementation).
- Copyright (C) 1997, 2001-2002, 2004-2015 Free Software Foundation, Inc.
+ Copyright (C) 1997, 2001-2002, 2004-2014 Free Software Foundation, Inc.
Contributed by Simon Josefsson .
This program is free software; you can redistribute it and/or modify
diff --git a/lib/getlogin.c b/lib/getlogin.c
index 5fc54b851..f8cfe5d78 100644
--- a/lib/getlogin.c
+++ b/lib/getlogin.c
@@ -1,6 +1,6 @@
/* Provide a working getlogin for systems which lack it.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getpeername.c b/lib/getpeername.c
index ce2e105a6..e5b3eaea3 100644
--- a/lib/getpeername.c
+++ b/lib/getpeername.c
@@ -1,6 +1,6 @@
/* getpeername.c --- wrappers for Windows getpeername function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getsockname.c b/lib/getsockname.c
index 6f24e7c18..d26bae592 100644
--- a/lib/getsockname.c
+++ b/lib/getsockname.c
@@ -1,6 +1,6 @@
/* getsockname.c --- wrappers for Windows getsockname function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/getsockopt.c b/lib/getsockopt.c
index ccd2e6fe7..0b2fb2b73 100644
--- a/lib/getsockopt.c
+++ b/lib/getsockopt.c
@@ -1,6 +1,6 @@
/* getsockopt.c --- wrappers for Windows getsockopt function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/gettext.h b/lib/gettext.h
index 3770ca0e8..330d8dad4 100644
--- a/lib/gettext.h
+++ b/lib/gettext.h
@@ -1,5 +1,5 @@
/* Convenience header for conditional use of GNU .
- Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2015 Free Software
+ Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2014 Free Software
Foundation, Inc.
This program is free software; you can redistribute it and/or modify
diff --git a/lib/gettimeofday.c b/lib/gettimeofday.c
index a615f3b25..c4e40fbe9 100644
--- a/lib/gettimeofday.c
+++ b/lib/gettimeofday.c
@@ -1,6 +1,6 @@
/* Provide gettimeofday for systems that don't have it or for which it's broken.
- Copyright (C) 2001-2003, 2005-2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2001-2003, 2005-2007, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/glthread/lock.h b/lib/glthread/lock.h
index 1aafd8bb4..66c78a6cd 100644
--- a/lib/glthread/lock.h
+++ b/lib/glthread/lock.h
@@ -1,7 +1,7 @@
#ifndef SCM_GLTHREADS_H
#define SCM_GLTHREADS_H
-/* Copyright (C) 2014, 2015 Free Software Foundation, Inc.
+/* Copyright (C) 2014 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
diff --git a/lib/iconv.c b/lib/iconv.c
index 850c8b77b..a6dfed355 100644
--- a/lib/iconv.c
+++ b/lib/iconv.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 1999-2001, 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 1999-2001, 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv.in.h b/lib/iconv.in.h
index 6b3d245c9..ed95ed719 100644
--- a/lib/iconv.in.h
+++ b/lib/iconv.in.h
@@ -1,6 +1,6 @@
/* A GNU-like .
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv_close.c b/lib/iconv_close.c
index d728c0741..6e286734d 100644
--- a/lib/iconv_close.c
+++ b/lib/iconv_close.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconv_open-aix.h b/lib/iconv_open-aix.h
index 129e03084..a598e819d 100644
--- a/lib/iconv_open-aix.h
+++ b/lib/iconv_open-aix.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.0.3 */
/* Command-line: gperf -m 10 ./iconv_open-aix.gperf */
/* Computed positions: -k'4,$' */
diff --git a/lib/iconv_open-hpux.h b/lib/iconv_open-hpux.h
index 3bcff76a7..8f9f0a9ad 100644
--- a/lib/iconv_open-hpux.h
+++ b/lib/iconv_open-hpux.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.0.3 */
/* Command-line: gperf -m 10 ./iconv_open-hpux.gperf */
/* Computed positions: -k'4,$' */
@@ -272,7 +272,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+#ifdef __GNUC_STDC_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open-irix.h b/lib/iconv_open-irix.h
index 74ceb8f57..520582e52 100644
--- a/lib/iconv_open-irix.h
+++ b/lib/iconv_open-irix.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.0.3 */
/* Command-line: gperf -m 10 ./iconv_open-irix.gperf */
/* Computed positions: -k'1,$' */
@@ -172,7 +172,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+#ifdef __GNUC_STDC_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open-osf.h b/lib/iconv_open-osf.h
index 4d3576495..85e4c0f8f 100644
--- a/lib/iconv_open-osf.h
+++ b/lib/iconv_open-osf.h
@@ -1,4 +1,4 @@
-/* ANSI-C code produced by gperf version 3.0.4 */
+/* ANSI-C code produced by gperf version 3.0.3 */
/* Command-line: gperf -m 10 ./iconv_open-osf.gperf */
/* Computed positions: -k'4,$' */
@@ -251,7 +251,7 @@ static const struct mapping mappings[] =
#ifdef __GNUC__
__inline
-#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
+#ifdef __GNUC_STDC_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
diff --git a/lib/iconv_open.c b/lib/iconv_open.c
index 07174331f..fc19d44e2 100644
--- a/lib/iconv_open.c
+++ b/lib/iconv_open.c
@@ -1,5 +1,5 @@
/* Character set conversion.
- Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/iconveh.h b/lib/iconveh.h
index d02c97502..43b23eb39 100644
--- a/lib/iconveh.h
+++ b/lib/iconveh.h
@@ -1,5 +1,5 @@
/* Character set conversion handler type.
- Copyright (C) 2001-2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2001-2007, 2009-2014 Free Software Foundation, Inc.
Written by Bruno Haible.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/ignore-value.h b/lib/ignore-value.h
new file mode 100644
index 000000000..86cfad77b
--- /dev/null
+++ b/lib/ignore-value.h
@@ -0,0 +1,37 @@
+/* ignore a function return without a compiler warning
+
+ Copyright (C) 2008-2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+/* Written by Jim Meyering. */
+
+/* Use these functions to avoid a warning when using a function declared with
+ gcc's warn_unused_result attribute, but for which you really do want to
+ ignore the result. Traditionally, people have used a "(void)" cast to
+ indicate that a function's return value is deliberately unused. However,
+ if the function is declared with __attribute__((warn_unused_result)),
+ gcc issues a warning even with the cast.
+
+ Caution: most of the time, you really should heed gcc's warning, and
+ check the return value. However, in those exceptional cases in which
+ you're sure you know what you're doing, use this function.
+
+ For the record, here's one of the ignorable warnings:
+ "copy.c:233: warning: ignoring return value of 'fchown',
+ declared with attribute warn_unused_result". */
+
+static inline void ignore_value (int i) { (void) i; }
+static inline void ignore_ptr (void* p) { (void) p; }
+/* FIXME: what about aggregate types? */
diff --git a/lib/inet_ntop.c b/lib/inet_ntop.c
index 4620c2c09..462951968 100644
--- a/lib/inet_ntop.c
+++ b/lib/inet_ntop.c
@@ -1,6 +1,6 @@
/* inet_ntop.c -- convert IPv4 and IPv6 addresses from binary to text form
- Copyright (C) 2005-2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2006, 2008-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
index 1fc159a10..52ae31784 100644
--- a/lib/inet_pton.c
+++ b/lib/inet_pton.c
@@ -1,6 +1,6 @@
/* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form
- Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isfinite.c b/lib/isfinite.c
index 3792b9974..18c1d217f 100644
--- a/lib/isfinite.c
+++ b/lib/isfinite.c
@@ -1,5 +1,5 @@
/* Test for finite value (zero, subnormal, or normal, and not infinite or NaN).
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isinf.c b/lib/isinf.c
index 4c79580e3..217de79df 100644
--- a/lib/isinf.c
+++ b/lib/isinf.c
@@ -1,5 +1,5 @@
/* Test for positive or negative infinity.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnan.c b/lib/isnan.c
index d70c9348a..1557733bf 100644
--- a/lib/isnan.c
+++ b/lib/isnan.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnand-nolibm.h b/lib/isnand-nolibm.h
index f5204f328..b0498ef08 100644
--- a/lib/isnand-nolibm.h
+++ b/lib/isnand-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnand.c b/lib/isnand.c
index 34e604fb7..11efbf8d8 100644
--- a/lib/isnand.c
+++ b/lib/isnand.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanf-nolibm.h b/lib/isnanf-nolibm.h
index b3a280c70..9e2aa2f54 100644
--- a/lib/isnanf-nolibm.h
+++ b/lib/isnanf-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanf.c b/lib/isnanf.c
index 7d21bddb4..c7a66ca3a 100644
--- a/lib/isnanf.c
+++ b/lib/isnanf.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanl-nolibm.h b/lib/isnanl-nolibm.h
index 48a02b24e..9cf090caa 100644
--- a/lib/isnanl-nolibm.h
+++ b/lib/isnanl-nolibm.h
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/isnanl.c b/lib/isnanl.c
index 9ec4eb31f..dbf9d5dd1 100644
--- a/lib/isnanl.c
+++ b/lib/isnanl.c
@@ -1,5 +1,5 @@
/* Test for NaN that does not need libm.
- Copyright (C) 2007, 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2009-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/itold.c b/lib/itold.c
index 4963b9b2d..136742eab 100644
--- a/lib/itold.c
+++ b/lib/itold.c
@@ -1,5 +1,5 @@
/* Replacement for 'int' to 'long double' conversion routine.
- Copyright (C) 2011-2015 Free Software Foundation, Inc.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
Written by Bruno Haible , 2011.
This program is free software: you can redistribute it and/or modify
diff --git a/lib/langinfo.in.h b/lib/langinfo.in.h
index cf992cede..f4a281a33 100644
--- a/lib/langinfo.in.h
+++ b/lib/langinfo.in.h
@@ -1,5 +1,5 @@
/* Substitute for and wrapper around .
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -49,10 +49,7 @@ typedef int nl_item;
# define CODESET 10000
/* nl_langinfo items of the LC_NUMERIC category */
# define RADIXCHAR 10001
-# define DECIMAL_POINT RADIXCHAR
# define THOUSEP 10002
-# define THOUSANDS_SEP THOUSEP
-# define GROUPING 10114
/* nl_langinfo items of the LC_TIME category */
# define D_T_FMT 10003
# define D_FMT 10004
@@ -105,21 +102,6 @@ typedef int nl_item;
# define ALT_DIGITS 10051
/* nl_langinfo items of the LC_MONETARY category */
# define CRNCYSTR 10052
-# define CURRENCY_SYMBOL CRNCYSTR
-# define INT_CURR_SYMBOL 10100
-# define MON_DECIMAL_POINT 10101
-# define MON_THOUSANDS_SEP 10102
-# define MON_GROUPING 10103
-# define POSITIVE_SIGN 10104
-# define NEGATIVE_SIGN 10105
-# define FRAC_DIGITS 10106
-# define INT_FRAC_DIGITS 10107
-# define P_CS_PRECEDES 10108
-# define N_CS_PRECEDES 10109
-# define P_SEP_BY_SPACE 10110
-# define N_SEP_BY_SPACE 10111
-# define P_SIGN_POSN 10112
-# define N_SIGN_POSN 10113
/* nl_langinfo items of the LC_MESSAGES category */
# define YESEXPR 10053
# define NOEXPR 10054
diff --git a/lib/link.c b/lib/link.c
index ebdd9f4ef..9db1f8cef 100644
--- a/lib/link.c
+++ b/lib/link.c
@@ -1,6 +1,6 @@
/* Emulate link on platforms that lack it, namely native Windows platforms.
- Copyright (C) 2009-2015 Free Software Foundation, Inc.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/listen.c b/lib/listen.c
index 7cf092587..912f1b7a7 100644
--- a/lib/listen.c
+++ b/lib/listen.c
@@ -1,6 +1,6 @@
/* listen.c --- wrappers for Windows listen function
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
diff --git a/lib/localcharset.c b/lib/localcharset.c
index 6dffe3454..7f09567ce 100644
--- a/lib/localcharset.c
+++ b/lib/localcharset.c
@@ -1,6 +1,6 @@
/* Determine a canonical name for the current locale's character encoding.
- Copyright (C) 2000-2006, 2008-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2006, 2008-2014 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@@ -34,7 +34,6 @@
#if defined _WIN32 || defined __WIN32__
# define WINDOWS_NATIVE
-# include