unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* Adding Identities to Peval
@ 2012-02-16  1:29 Noah Lavine
  2012-02-16  2:32 ` Mark H Weaver
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Noah Lavine @ 2012-02-16  1:29 UTC (permalink / raw)
  To: guile-devel

[-- Attachment #1: Type: text/plain, Size: 2263 bytes --]

Hello,

I've been working on a patch to add a new sort of optimization to
peval, and I think it's almost ready. It's based on some of the ideas
in "Environment Analysis of Higher-Order Languages".

The goal is to recognize when two quantities are equal even when we
don't know what they are. My working example has been this expression:

(let* ((x (random))
       (y x))
  (eq? x y))

The patch attached to this message lets peval optimize that to

(begin (random) #t)

This happens through objects called 'identities'. We make a new
identity for every constant and the result of every procedure call.
Identities are associated with values, not variables, so x and y above
have the same identity. When it looks at the eq?, peval notices that x
and y have the same identity, and concludes that they must be eq? even
without knowing their value.

The patch adds some members to the <operand> record type to store an
identity for the operand, and an identity-visit function that matches
the visit function used to get values. It also has a few utility
functions and the definition of a new record type <identity> with no
members. The logic added is pretty minimal. I tested it by running
check-guile. It passes about the same number of tests as it did before
I added the patch.

There's one glaring wart. The identity checking is activiated by calls
to (toplevel 'eq?). Clearly, it should be activated by calls to the
primitive 'eq? (and eqv? and equal?). The reason it's not is that my
example above compiles to (call (toplevel-ref 'eq?) ...), and I don't
know how to make it turn into a (primcall 'eq? ...). I tried a few
variants, but they didn't work. What test code should I be using to
produce a primcall?

However, that problem is easy to fix, and besides that I believe the
patch is correct.

This also relates to my earlier ideas for a compiler. After thinking
about it more, I'm not sure I like the direction I took in the
wip-compiler branch, so I decided to start working on peval instead.
This patch has an ulterior motive - besides adding a new optimization
itself, it uses a lot of the infrastructure you'd want for more
aggressive optimization like type-checking. So part of the purpose of
this was to learn how to do that.

What do you think?
Noah

[-- Attachment #2: 0001-Add-Identity-Optimization.patch --]
[-- Type: application/octet-stream, Size: 8547 bytes --]

From 8ef122b4befc1236ee1ff316088d1c109b6b1a07 Mon Sep 17 00:00:00 2001
From: Noah Lavine <noah.b.lavine@gmail.com>
Date: Wed, 15 Feb 2012 20:15:41 -0500
Subject: [PATCH] Add Identity Optimization

* language/tree-il/peval.scm: add 'identities', which allow peval
  to prove that two things are equal without knowing their values.
---
 module/language/tree-il/peval.scm |   89 +++++++++++++++++++++++++++++++-----
 1 files changed, 76 insertions(+), 13 deletions(-)

diff --git a/module/language/tree-il/peval.scm b/module/language/tree-il/peval.scm
index 9aac24c..e2fc16b 100644
--- a/module/language/tree-il/peval.scm
+++ b/module/language/tree-il/peval.scm
@@ -46,14 +46,14 @@
 
 ;; First, some helpers.
 ;;
-(define-syntax *logging* (identifier-syntax #f))
+;; (define-syntax *logging* (identifier-syntax #f))
 
 ;; For efficiency we define *logging* to inline to #f, so that the call
 ;; to log* gets optimized out.  If you want to log, uncomment these
 ;; lines:
 ;;
-;; (define %logging #f)
-;; (define-syntax *logging* (identifier-syntax %logging))
+(define %logging #f)
+(define-syntax *logging* (identifier-syntax %logging))
 ;;
 ;; Then you can change %logging at runtime.
 
@@ -285,7 +285,8 @@
 ;; 
 (define-record-type <operand>
   (%make-operand var sym visit source visit-count residualize?
-                 copyable? residual-value constant-value)
+                 copyable? residual-value constant-value
+                 identity identity-visit identifiable?)
   operand?
   (var operand-var)
   (sym operand-sym)
@@ -295,18 +296,23 @@
   (residualize? operand-residualize? set-operand-residualize?!)
   (copyable? operand-copyable? set-operand-copyable?!)
   (residual-value operand-residual-value %set-operand-residual-value!)
-  (constant-value operand-constant-value set-operand-constant-value!))
+  (constant-value operand-constant-value set-operand-constant-value!)
+  (identity %operand-identity set-operand-identity!)
+  (identity-visit %operand-identity-visit)
+  (identifiable? operand-identifiable? set-operand-identifiable?!))
 
-(define* (make-operand var sym #:optional source visit)
+(define* (make-operand var sym #:optional source visit id-visit)
   ;; Bind SYM to VAR, with value SOURCE.  Bound operands are considered
   ;; copyable until we prove otherwise.  If we have a source expression,
   ;; truncate it to one value.  Copy propagation does not work on
   ;; multiply-valued expressions.
   (let ((source (and=> source truncate-values)))
-    (%make-operand var sym visit source 0 #f (and source #t) #f #f)))
+    (%make-operand var sym visit source 0 #f (and source #t) #f #f
+                   #f id-visit #t)))
 
-(define (make-bound-operands vars syms sources visit)
-  (map (lambda (x y z) (make-operand x y z visit)) vars syms sources))
+(define (make-bound-operands vars syms sources visit id-visit)
+  (map (lambda (x y z) (make-operand x y z visit id-visit))
+       vars syms sources))
 
 (define (make-unbound-operands vars syms)
   (map make-operand vars syms))
@@ -322,6 +328,15 @@
     (else
      val))))
 
+(define (operand-identity op)
+  (and (operand-identifiable? op)
+       (or (operand-constant-value op)
+           (%operand-identity op))))
+
+(define-record-type <identity>
+  (make-identity)
+  identity?)
+
 (define* (visit-operand op counter ctx #:optional effort-limit size-limit)
   ;; Peval is O(N) in call sites of the source program.  However,
   ;; visiting an operand can introduce new call sites.  If we visit an
@@ -348,6 +363,17 @@
          (lambda ()
            (set-operand-visit-count! op (1- (operand-visit-count op)))))))
 
+(define (visit-operand-for-identity op)
+  (and (zero? (operand-visit-count op))
+       (dynamic-wind
+           (lambda ()
+             (set-operand-visit-count! op (1+ (operand-visit-count op))))
+           (lambda ()
+             (and (operand-source op)
+                  ((%operand-identity-visit op) (operand-source op))))
+           (lambda ()
+             (set-operand-visit-count! op (1- (operand-visit-count op)))))))
+
 ;; A helper for constant folding.
 ;;
 (define (types-check? primitive-name args)
@@ -602,6 +628,28 @@ top-level bindings from ENV and return the resulting expression."
          (and (loop tag) (loop body) (loop handler)))
         (_ #f))))
 
+  ;; return an identity for x, or #f if we can't
+  (define (get-identity x env)
+    (match x
+      (($ <const>) x)
+      (($ <lexical-ref> _ _ gensym)
+       (let ((op (cdr (vhash-assq gensym env))))
+         (cond ((not (operand-identifiable? op)) #f)
+               ((var-set? (operand-var op))
+                (set-operand-identifiable?! op #f)
+                #f)
+               ((operand-identity op) => identity)
+               ((visit-operand-for-identity op) =>
+                (lambda (id)
+                  (set-operand-identity! op id)
+                  id))
+               (else (set-operand-identity! op #f)
+                     (set-operand-identifiable?! op #f)
+                     #f))))
+      (($ <call>) (make-identity))
+      (($ <primcall>) (make-identity))
+      (_ #f)))
+
   (define (prune-bindings ops in-order? body counter ctx build-result)
     ;; This helper handles both `let' and `letrec'/`fix'.  In the latter
     ;; cases we need to make sure that if referenced binding A needs
@@ -832,7 +880,9 @@ top-level bindings from ENV and return the resulting expression."
               (new (fresh-gensyms vars))
               (ops (make-bound-operands vars new vals
                                         (lambda (exp counter ctx)
-                                          (loop exp env counter ctx))))
+                                          (loop exp env counter ctx))
+                                        (lambda (exp)
+                                          (get-identity exp env))))
               (env (fold extend-env env gensyms ops))
               (body (loop body env counter ctx)))
          (cond
@@ -861,9 +911,10 @@ top-level bindings from ENV and return the resulting expression."
        ;; an environment that includes the operands.
        (letrec* ((visit (lambda (exp counter ctx)
                           (loop exp env* counter ctx)))
+                 (id-visit (lambda (exp) (get-identity exp env)))
                  (vars (map lookup-var gensyms))
                  (new (fresh-gensyms vars))
-                 (ops (make-bound-operands vars new vals visit))
+                 (ops (make-bound-operands vars new vals visit id-visit))
                  (env* (fold extend-env env gensyms ops))
                  (body* (visit body counter ctx)))
          (if (and (const? body*) (every constant-expression? vals))
@@ -878,9 +929,10 @@ top-level bindings from ENV and return the resulting expression."
       (($ <fix> src names gensyms vals body)
        (letrec* ((visit (lambda (exp counter ctx)
                           (loop exp env* counter ctx)))
+                 (id-visit (lambda (exp) (get-identity exp env)))
                  (vars (map lookup-var gensyms))
                  (new (fresh-gensyms vars))
-                 (ops (make-bound-operands vars new vals visit))
+                 (ops (make-bound-operands vars new vals visit id-visit))
                  (env* (fold extend-env env gensyms ops))
                  (body* (visit body counter ctx)))
          (if (const? body*)
@@ -1109,12 +1161,23 @@ top-level bindings from ENV and return the resulting expression."
       (($ <primcall> src name args)
        (make-primcall src name (map for-value args)))
 
+      (($ <call> src ($ <toplevel-ref> t-src 'eq?) (a b))
+       (log 'visit-eq? (list 'eq? a b))
+       (let ((id-a (get-identity a env))
+             (id-b (get-identity b env)))
+         (log 'id-a id-a 'id-b id-b 'eq? (eq? id-a id-b))
+         (if (and id-a id-b)
+             (make-const #f (eq? id-a id-b))
+             (make-primcall src 'eq? (list a b)))))
+      
       (($ <call> src orig-proc orig-args)
        ;; todo: augment the global env with specialized functions
        (let ((proc (visit orig-proc 'operator)))
          (match proc
            (($ <primitive-ref> _ name)
-            (for-tail (make-primcall src name orig-args)))
+            (let ((rep (make-primcall src name orig-args)))
+              (log 'replacing-call exp rep)
+              (for-tail rep)))
            (($ <lambda> _ _
                ($ <lambda-case> _ req opt #f #f inits gensyms body #f))
             ;; Simple case: no rest, no keyword arguments.
-- 
1.7.6


^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2012-02-20 20:25 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-16  1:29 Adding Identities to Peval Noah Lavine
2012-02-16  2:32 ` Mark H Weaver
2012-02-16  6:00 ` David Kastrup
2012-02-16  6:39   ` David Kastrup
2012-02-16  8:14     ` David Kastrup
2012-02-16  9:36 ` Andy Wingo
2012-02-16 13:18   ` Noah Lavine
2012-02-16 15:06     ` Andy Wingo
2012-02-16 17:33       ` Andy Wingo
2012-02-17  2:22       ` Noah Lavine
2012-02-17  8:13         ` Andy Wingo
2012-02-18 16:20           ` Noah Lavine
2012-02-19  9:53             ` Andy Wingo
2012-02-20 20:25               ` Noah Lavine

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).