diff --git a/module/Makefile.am b/module/Makefile.am
index 4ca70c2e8..7b45d9043 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -131,6 +131,7 @@ CPS_LANG_SOURCES = \
language/cps/dfg.scm \
language/cps/effects-analysis.scm \
language/cps/elide-values.scm \
+ language/cps/intset.scm \
language/cps/nameset.scm \
language/cps/primitives.scm \
language/cps/prune-bailouts.scm \
diff --git a/module/language/cps/intset.scm b/module/language/cps/intset.scm
new file mode 100644
index 000000000..8bda290c1
--- /dev/null
+++ b/module/language/cps/intset.scm
@@ -0,0 +1,417 @@
+;;; Functional name maps
+;;; 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 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 persistent, functional data structure representing a set of
+;;; integers as a tree whose branches are vectors and whose leaves are
+;;; fixnums. Intsets are careful to preserve sub-structure, in the
+;;; sense of eq?, whereever possible.
+;;;
+;;; Code:
+
+(define-module (language cps intset)
+ #:use-module (rnrs bytevectors)
+ #:use-module (srfi srfi-9)
+ #:use-module (ice-9 match)
+ #:export (empty-intset
+ intset?
+ intset-add
+ intset-remove
+ intset-ref
+ intset-next
+ intset-union
+ intset-intersect))
+
+(define-syntax-rule (define-inline name val)
+ (define-syntax name (identifier-syntax val)))
+
+(define-inline *leaf-bits* 5)
+(define-inline *leaf-size* (ash 1 *leaf-bits*))
+(define-inline *leaf-mask* (1- *leaf-size*))
+(define-inline *branch-bits* 3)
+(define-inline *branch-size* (ash 1 *branch-bits*))
+(define-inline *branch-mask* (1- *branch-size*))
+
+(define-record-type
+ (make-intset min shift root)
+ intset?
+ (min intset-min)
+ (shift intset-shift)
+ (root intset-root))
+
+(define (new-leaf) 0)
+(define-inlinable (clone-leaf-and-set leaf i val)
+ (if val
+ (if leaf
+ (logior leaf (ash 1 i))
+ (ash 1 i))
+ (if leaf
+ (logand leaf (lognot (ash 1 i)))
+ #f)))
+(define (leaf-empty? leaf)
+ (zero? leaf))
+
+(define (new-branch)
+ (make-vector *branch-size* #f))
+(define (clone-branch-and-set branch i elt)
+ (let ((new (new-branch)))
+ (when branch (vector-move-left! branch 0 *branch-size* new 0))
+ (vector-set! new i elt)
+ new))
+(define (branch-empty? branch)
+ (let lp ((i 0))
+ (or (= i *branch-size*)
+ (and (not (vector-ref branch i))
+ (lp (1+ i))))))
+
+(define (round-down min shift)
+ (logand min (lognot (1- (ash 1 shift)))))
+
+(define empty-intset (make-intset 0 *leaf-bits* #f))
+
+(define (add-level min shift root)
+ (let* ((shift* (+ shift *branch-bits*))
+ (min* (round-down min shift*))
+ (idx (logand (ash (- min min*) (- shift)) *branch-mask*)))
+ (make-intset min* shift* (clone-branch-and-set #f idx root))))
+
+(define (make-intset/prune min shift root)
+ (if (= shift *leaf-bits*)
+ (make-intset min shift root)
+ (let lp ((i 0) (elt #f))
+ (cond
+ ((< i *branch-size*)
+ (if (vector-ref root i)
+ (if elt
+ (make-intset min shift root)
+ (lp (1+ i) i))
+ (lp (1+ i) elt)))
+ (elt
+ (let ((shift (- shift *branch-bits*)))
+ (make-intset/prune (+ min (ash elt shift))
+ shift
+ (vector-ref root elt))))
+ ;; Shouldn't be reached...
+ (else empty-intset)))))
+
+(define (intset-add bs i)
+ (define (adjoin i shift root)
+ (cond
+ ((= shift *leaf-bits*)
+ (let ((idx (logand i *leaf-mask*)))
+ (if (and root (logbit? idx root))
+ root
+ (clone-leaf-and-set root idx #t))))
+ (else
+ (let* ((shift (- shift *branch-bits*))
+ (idx (logand (ash i (- shift)) *branch-mask*))
+ (node (and root (vector-ref root idx)))
+ (new-node (adjoin i shift node)))
+ (if (eq? node new-node)
+ root
+ (clone-branch-and-set root idx new-node))))))
+ (match bs
+ (($ min shift root)
+ (cond
+ ((not root)
+ ;; Add first element.
+ (let ((min (round-down i shift)))
+ (make-intset min *leaf-bits*
+ (adjoin (- i min) *leaf-bits* root))))
+ ((and (<= min i) (< i (+ min (ash 1 shift))))
+ ;; Add element to set; level will not change.
+ (let ((old-root root)
+ (root (adjoin (- i min) shift root)))
+ (if (eq? root old-root)
+ bs
+ (make-intset min shift root))))
+ ((< i min)
+ ;; Rebuild the tree by unioning two intsets.
+ (intset-union (intset-add empty-intset i) bs))
+ (else
+ ;; Add a new level and try again.
+ (intset-add (add-level min shift root) i))))))
+
+(define (intset-remove bs i)
+ (define (remove i shift root)
+ (cond
+ ((= shift *leaf-bits*)
+ (let ((idx (logand i *leaf-mask*)))
+ (if (logbit? idx root)
+ (let ((root (clone-leaf-and-set root idx #f)))
+ (and (not (leaf-empty? root)) root))
+ root)))
+ (else
+ (let* ((shift (- shift *branch-bits*))
+ (idx (logand (ash i (- shift)) *branch-mask*)))
+ (cond
+ ((vector-ref root idx)
+ => (lambda (node)
+ (let ((new-node (remove i shift node)))
+ (if (eq? node new-node)
+ root
+ (let ((root (clone-branch-and-set root idx new-node)))
+ (and (or new-node (not (branch-empty? root)))
+ root))))))
+ (else root))))))
+ (match bs
+ (($ min shift root)
+ (cond
+ ((not root) bs)
+ ((and (<= min i) (< i (+ min (ash 1 shift))))
+ ;; Add element to set; level will not change.
+ (let ((old-root root)
+ (root (remove (- i min) shift root)))
+ (if (eq? root old-root)
+ bs
+ (make-intset/prune min shift root))))
+ (else bs)))))
+
+(define (intset-ref bs i)
+ (match bs
+ (($ min shift root)
+ (and (<= min i) (< i (+ min (ash 1 shift)))
+ (let ((i (- i min)))
+ (let lp ((node root) (shift shift))
+ (and node
+ (if (= shift *leaf-bits*)
+ (logbit? (logand i *leaf-mask*) node)
+ (let* ((shift (- shift *branch-bits*))
+ (idx (logand (ash i (- shift)) *branch-mask*)))
+ (lp (vector-ref node idx) shift))))))))))
+
+(define (intset-next bs 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 (< idx *leaf-size*)
+ (lp idx))))))
+ (define (visit-branch node shift i)
+ (let lp ((i i) (idx (logand (ash i (- shift)) *branch-mask*)))
+ (and (< idx *branch-size*)
+ (or (visit-node (vector-ref node idx) shift i)
+ (let ((inc (ash 1 shift)))
+ (lp (+ (round-down i shift) inc) (1+ idx)))))))
+ (define (visit-node node shift i)
+ (and node
+ (if (= shift *leaf-bits*)
+ (visit-leaf node i)
+ (visit-branch node (- shift *branch-bits*) i))))
+ (match bs
+ (($ min shift root)
+ (let ((i (if (and i (< min i))
+ (- i min)
+ 0)))
+ (and (< i (ash 1 shift))
+ (let ((i (visit-node root shift i)))
+ (and i (+ min i))))))))
+
+(define (intset-size shift root)
+ (cond
+ ((not root) 0)
+ ((= *leaf-bits* shift) *leaf-size*)
+ (else
+ (let lp ((i (1- *branch-size*)))
+ (let ((node (vector-ref root i)))
+ (if node
+ (let ((shift (- shift *branch-bits*)))
+ (+ (intset-size shift node)
+ (* i (ash 1 shift))))
+ (lp (1- i))))))))
+
+(define (intset-union a b)
+ ;; Union leaves.
+ (define (union-leaves a b)
+ (logior (or a 0) (or b 0)))
+ ;; Union A and B from index I; the result will be fresh.
+ (define (union-branches/fresh shift a b i fresh)
+ (let lp ((i 0))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (vector-set! fresh i (union shift a-child b-child))
+ (lp (1+ i))))
+ (else fresh))))
+ ;; Union A and B from index I; the result may be eq? to A.
+ (define (union-branches/a shift a b i)
+ (let lp ((i i))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (if (eq? a-child b-child)
+ (lp (1+ i))
+ (let ((child (union shift a-child b-child)))
+ (cond
+ ((eq? a-child child)
+ (lp (1+ i)))
+ (else
+ (let ((result (clone-branch-and-set a i child)))
+ (union-branches/fresh shift a b (1+ i) result))))))))
+ (else a))))
+ ;; Union A and B; the may could be eq? to either.
+ (define (union-branches shift a b)
+ (let lp ((i 0))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (if (eq? a-child b-child)
+ (lp (1+ i))
+ (let ((child (union shift a-child b-child)))
+ (cond
+ ((eq? a-child child)
+ (union-branches/a shift a b (1+ i)))
+ ((eq? b-child child)
+ (union-branches/a shift b a (1+ i)))
+ (else
+ (let ((result (clone-branch-and-set a i child)))
+ (union-branches/fresh shift a b (1+ i) result))))))))
+ ;; Seems they are the same but not eq?. Odd.
+ (else a))))
+ (define (union shift a-node b-node)
+ (cond
+ ((not a-node) b-node)
+ ((not b-node) a-node)
+ ((eq? a-node b-node) a-node)
+ ((= shift *leaf-bits*) (union-leaves a-node b-node))
+ (else (union-branches (- shift *branch-bits*) a-node b-node))))
+ (match (cons a b)
+ ((($ a-min a-shift a-root) . ($ b-min b-shift b-root))
+ (cond
+ ((not (= b-shift a-shift))
+ ;; Hoist the set with the lowest shift to meet the one with the
+ ;; higher shift.
+ (if (< b-shift a-shift)
+ (intset-union a (add-level b-min b-shift b-root))
+ (intset-union (add-level a-min a-shift a-root) b)))
+ ((not (= b-min a-min))
+ ;; Nodes at the same shift but different minimums will cover
+ ;; disjoint ranges (due to the round-down call on min). Hoist
+ ;; both until they cover the same range.
+ (intset-union (add-level a-min a-shift a-root)
+ (add-level b-min b-shift b-root)))
+ (else
+ ;; At this point, A and B cover the same range.
+ (let ((root (union a-shift a-root b-root)))
+ (cond
+ ((eq? root a-root) a)
+ ((eq? root b-root) b)
+ (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))
+ ;; 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))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (vector-set! fresh i (intersect shift a-child b-child))
+ (lp (1+ i))))
+ ((branch-empty? fresh) #f)
+ (else fresh))))
+ ;; Intersect A and B from index I; the result may be eq? to A.
+ (define (intersect-branches/a shift a b i)
+ (let lp ((i i))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (if (eq? a-child b-child)
+ (lp (1+ i))
+ (let ((child (intersect shift a-child b-child)))
+ (cond
+ ((eq? a-child child)
+ (lp (1+ i)))
+ (else
+ (let ((result (clone-branch-and-set a i child)))
+ (intersect-branches/fresh shift a b (1+ i) result))))))))
+ (else a))))
+ ;; Intersect A and B; the may could be eq? to either.
+ (define (intersect-branches shift a b)
+ (let lp ((i 0))
+ (cond
+ ((< i *branch-size*)
+ (let* ((a-child (vector-ref a i))
+ (b-child (vector-ref b i)))
+ (if (eq? a-child b-child)
+ (lp (1+ i))
+ (let ((child (intersect shift a-child b-child)))
+ (cond
+ ((eq? a-child child)
+ (intersect-branches/a shift a b (1+ i)))
+ ((eq? b-child child)
+ (intersect-branches/a shift b a (1+ i)))
+ (else
+ (let ((result (clone-branch-and-set a i child)))
+ (intersect-branches/fresh shift a b (1+ i) result))))))))
+ ;; Seems they are the same but not eq?. Odd.
+ (else a))))
+ (define (intersect shift a-node b-node)
+ (cond
+ ((or (not a-node) (not b-node)) #f)
+ ((eq? a-node b-node) a-node)
+ ((= shift *leaf-bits*) (intersect-leaves a-node b-node))
+ (else (intersect-branches (- shift *branch-bits*) a-node b-node))))
+ (match (cons a b)
+ ((($ a-min a-shift a-root) . ($ b-min b-shift b-root))
+ (cond
+ ((< a-min b-min)
+ ;; Make A have the higher min.
+ (intset-intersect b a))
+ ((< b-min a-min)
+ (cond
+ ((<= b-shift a-shift)
+ ;; If B has a lower shift and a lower min, it is disjoint. If
+ ;; it has the same shift and a different min, it is also
+ ;; disjoint.
+ empty-intset)
+ (else
+ (let* ((b-shift (- b-shift *branch-bits*))
+ (b-idx (ash (- a-min b-min) (- b-shift))))
+ (if (>= b-idx *branch-size*)
+ ;; A has a lower shift, but it not within B.
+ empty-intset
+ (intset-intersect a
+ (make-intset (+ b-min (ash b-idx b-shift))
+ b-shift
+ (vector-ref b-root b-idx))))))))
+ ((< b-shift a-shift)
+ ;; Make A have the lower shift.
+ (intset-intersect b a))
+ ((< a-shift b-shift)
+ ;; A and B have the same min but a different shift. Recurse down.
+ (intset-intersect a
+ (make-intset b-min
+ (- b-shift *branch-bits*)
+ (vector-ref b-root 0))))
+ (else
+ ;; At this point, A and B cover the same range.
+ (let ((root (intersect a-shift a-root b-root)))
+ (cond
+ ((eq? root a-root) a)
+ ((eq? root b-root) b)
+ (else (make-intset/prune a-min a-shift root)))))))))