unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
* Special variables to relax boxing
@ 2013-03-19 22:05 Stefan Israelsson Tampe
  2013-03-21  6:00 ` Mark H Weaver
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Israelsson Tampe @ 2013-03-19 22:05 UTC (permalink / raw)
  To: guile-devel

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

Hi,

I wouldl like to start a discussion of introducing a way to mark
variables as special, and by that mean that set! variables does not
nessesary get boxed. This is a deviation from the scheme standard but
it's pretty useful if one want to set! local variables but make sure
that the state of the function can be stored in a continuation and
later restart at the same state.

This patch introduces a macro (mark-as-special var), this will mark
var as a special variable in the above meaning, please look at the
example in special.scm for more info.

There is a preleminary patch following the document against stable-2.0
that we can use as a discussion point for know about this feature.

Best Greetings
Stefan


[-- Attachment #2: special.scm --]
[-- Type: text/x-scheme, Size: 3908 bytes --]

(use-modules (system vm special-variable))
;; This makes sure to treat s special and not box it
(define (g x) 
  (let ((s 0)) 
    (mark-as-special s)  
    (let lp ((i 0)) 
      (if (< i 10000000) 
	  (begin (set! s (+ i s)) 
		 (lp (+ i 1))) 
	  s))))

#|
,x g

0    (assert-nargs-ee/locals 17)     ;; 1 arg, 2 locals
   2    (make-int8:0)                   ;; 0
   3    (local-set 1)                   ;; `s'
   5    (new-frame)                     
   6    (toplevel-ref 1)                ;; `((system vm special-variable) special #f)'
   8    (local-ref 1)                   ;; `s'
  10    (mv-call 1 :L583)               ;; MV -> 20
  15    (drop)                          
  16    (br :L584)                      ;; -> 23
  20    (truncate-values 0 0)           
  23    (br :L585)                      ;; -> 56
  27    (local-ref 2)                   ;; `i'
  29    (make-int16 3 232)              ;; 1000
  32    (lt?)                           
  33    (br-if-not :L586)               ;; -> 53
  37    (local-ref 2)                   ;; `i'
  39    (local-ref 1)                   ;; `s'
  41    (add)                           
  42    (local-set 1)                   ;; `s'
  44    (local-ref 2)                   ;; `i'
  46    (add1)                          
  47    (local-set 2)                   ;; `i'
  49    (br :L587)                      ;; -> 27
  53    (local-ref 1)                   ;; `s'
  55    (return)                        
  56    (make-int8:0)                   ;; 0
  57    (local-set 2)                   
  59    (br :L587)                      ;; -> 27

|#
;; This makes sure to treat s as special but we cannot box it because it's 
;; referenced in closure, set! + closure  = boxed
(define (g2 x) 
  (let ((s 0)) 
    (mark-as-special s)  
    (let lp ((i 0)) 
      (if (< i 10000000) 
	  (begin (set! s (+ i s)) 
		 (lp (+ i 1))) 
	  (lambda () s)))))


#|
0    (assert-nargs-ee/locals 17)     ;; 1 arg, 2 locals
   2    (make-int8:0)                   ;; 0                  at /home/stis/src/special.scm:45:2
   3    (box 1)                         
   5    (new-frame)                                           at /home/stis/src/special.scm:46:4
   6    (toplevel-ref 1)                ;; `((system vm special-variable) special #f)'
   8    (local-boxed-ref 1)             ;; `s'
  10    (mv-call 1 :L824)               ;; MV -> 20
  15    (drop)                          
  16    (br :L825)                      ;; -> 23
  20    (truncate-values 0 0)           
  23    (br :L826)                      ;; -> 67              at /home/stis/src/special.scm:47:4
  27    (local-ref 2)                   ;; `i'
  29    (make-uint64 0 0 0 0 0 152 150 128);; 10000000
  38    (lt?)                                                 at /home/stis/src/special.scm:48:10
  39    (br-if-not :L827)               ;; -> 59              at /home/stis/src/special.scm:48:6
  43    (local-ref 2)                   ;; `i'
  45    (local-boxed-ref 1)             ;; `s'
  47    (add)                                                 at /home/stis/src/special.scm:49:25
  48    (local-boxed-set 1)             ;; `s'                at /home/stis/src/special.scm:49:17
  50    (local-ref 2)                   ;; `i'
  52    (add1)                                                at /home/stis/src/special.scm:50:21
  53    (local-set 2)                   ;; `i'
  55    (br :L828)                      ;; -> 27              at /home/stis/src/special.scm:50:17
  59    (object-ref 2)                  ;; #<procedure 13bbe80 at /home/stis/src/special.scm:51:10 ()>
  61    (local-ref 1)                   ;; `s'
  63    (make-closure 0 1)              
  66    (return)                                              at /home/stis/src/special.scm:47:4
  67    (make-int8:0)                   ;; 0
  68    (local-set 2)                   
  70    (br :L828)                      ;; -> 27
|#

[-- Attachment #3: special.diff --]
[-- Type: text/x-patch, Size: 10680 bytes --]

diff --git a/module/Makefile.am b/module/Makefile.am
index c47d0b4..b07c342 100644
--- a/module/Makefile.am
+++ b/module/Makefile.am
@@ -101,6 +101,7 @@ SCHEME_LANG_SOURCES =						\
   language/scheme/decompile-tree-il.scm
 
 TREE_IL_LANG_SOURCES =						\
+  language/tree-il/special.scm					\
   language/tree-il/primitives.scm				\
   language/tree-il/effects.scm                                 	\
   language/tree-il/fix-letrec.scm                               \
@@ -337,6 +338,7 @@ OOP_SOURCES = \
   oop/goops/simple.scm
 
 SYSTEM_SOURCES =				\
+  system/vm/special-variable.scm                \
   system/vm/inspect.scm				\
   system/vm/coverage.scm			\
   system/vm/frame.scm				\
diff --git a/module/language/tree-il/analyze.scm b/module/language/tree-il/analyze.scm
index badce9f..50d64b8 100644
--- a/module/language/tree-il/analyze.scm
+++ b/module/language/tree-il/analyze.scm
@@ -151,7 +151,7 @@
     (hashq-set! res k v)
     res))
 
-(define (analyze-lexicals x)
+(define* (analyze-lexicals x #:optional (special-vars #f))
   ;; bound-vars: lambda -> (sym ...)
   ;;  all identifiers bound within a lambda
   (define bound-vars (make-hash-table))
@@ -159,6 +159,9 @@
   ;;  all identifiers referenced in a lambda, but not bound
   ;;  NB, this includes identifiers referenced by contained lambdas
   (define free-vars (make-hash-table))
+  ;; free-syms: sym -> #t
+  ;;   All variables that is free with respect to a lambda.
+  (define free-syms (make-hash-table))
   ;; assigned: sym -> #t
   ;;  variables that are assigned
   (define assigned (make-hash-table))
@@ -180,7 +183,7 @@
       (analyze! x new-proc (append labels labels-in-proc) #t #f))
     (define (recur x new-proc) (analyze! x new-proc '() tail? #f))
     (record-case x
-      ((<application> proc args)
+      ((<application> proc args)                              
        (apply lset-union eq? (step-tail-call proc args)
               (map step args)))
 
@@ -236,6 +239,9 @@
        (let ((free (recur body x)))
          (hashq-set! bound-vars x (reverse! (hashq-ref bound-vars x)))
          (hashq-set! free-vars x free)
+         (for-each (lambda (var)
+                     (hashq-set! free-syms var #t))
+                   free)
          free))
       
       ((<lambda-case> opt kw inits gensyms body alternate)
@@ -286,7 +292,8 @@
                      ;; recur/labels instead of recur
                      (hashq-set! bound-vars x '())
                      (let ((free (recur/labels body x gensyms)))
-                       (hashq-set! bound-vars x (reverse! (hashq-ref bound-vars x)))
+                       (hashq-set! bound-vars x 
+                                   (reverse! (hashq-ref bound-vars x)))
                        (hashq-set! free-vars x free)
                        free))))
                 vals))
@@ -330,7 +337,11 @@
                               (append (hashq-ref bound-vars val)
                                       (hashq-ref bound-vars proc)))
                   (hashq-remove! bound-vars val)
-                  (hashq-remove! free-vars val))))
+                  (hashq-remove! free-vars val))
+                ;; Else we will allocate a closure; register the free-syms
+                (for-each (lambda (sym)
+                            (hashq-set! free-syms sym #t))
+                          (hashq-ref free-vars val))))
           gensyms vals)
          (lset-difference eq?
                           (apply lset-union eq? body-refs var-refs)
@@ -395,7 +406,12 @@
              (begin
                (hashq-set! (hashq-ref allocation (car c))
                            x
-                           `(#f ,(hashq-ref assigned (car c)) . ,n))
+                           `(#f ,(and (hashq-ref assigned (car c))
+                                      (not (and special-vars
+                                                (hashq-ref special-vars (car c))
+                                                (not (hashq-ref free-syms 
+                                                                (car c))))))
+                                . ,n))
                (lp (cdr c) (1+ n)))))
       
        (let ((nlocs (allocate! body x 0))
@@ -427,7 +443,15 @@
               (begin
                 (hashq-set! allocation (car gensyms)
                             (make-hashq
-                             proc `(#t ,(hashq-ref assigned (car gensyms)) . ,n)))
+                             proc `(#t 
+                                    ,(and (hashq-ref assigned (car gensyms))
+                                          (not (and special-vars
+                                                    (hashq-ref special-vars 
+                                                               (car gensyms))
+                                                    (not (hashq-ref 
+                                                          free-syms 
+                                                          (car gensyms))))))
+                                       . ,n)))
                 (lp (cdr gensyms) (1+ n)))))
         (if alternate (allocate! alternate proc n) n)))
       
@@ -456,7 +480,12 @@
                    (hashq-set!
                     allocation v
                     (make-hashq proc
-                                `(#t ,(hashq-ref assigned v) . ,n)))
+                                `(#t ,(and (hashq-ref assigned v)
+                                      (not (and special-vars
+                                                (hashq-ref special-vars v)
+                                                (not (hashq-ref free-syms 
+                                                                v)))))
+                                     . ,n)))
                    (lp (cdr gensyms) (1+ n)))))))))
       
       ((<letrec> gensyms vals body)
@@ -471,7 +500,12 @@
                (hashq-set!
                 allocation v
                 (make-hashq proc
-                            `(#t ,(hashq-ref assigned v) . ,n)))
+                            `(#t ,(and (hashq-ref assigned v)
+                                      (not (and special-vars
+                                                (hashq-ref special-vars v)
+                                                (not (hashq-ref free-syms 
+                                                                v)))))
+                                 . ,n)))
                (lp (cdr gensyms) (1+ n))))))
 
       ((<fix> gensyms vals body)
diff --git a/module/language/tree-il/compile-glil.scm b/module/language/tree-il/compile-glil.scm
index e4df6e1..7321fb1 100644
--- a/module/language/tree-il/compile-glil.scm
+++ b/module/language/tree-il/compile-glil.scm
@@ -29,6 +29,7 @@
   #:use-module (language tree-il optimize)
   #:use-module (language tree-il canonicalize)
   #:use-module (language tree-il analyze)
+  #:use-module (language tree-il special)
   #:use-module ((srfi srfi-1) #:select (filter-map))
   #:export (compile-glil))
 
@@ -64,9 +65,10 @@
 
   (let* ((x (make-lambda (tree-il-src x) '()
                          (make-lambda-case #f '() #f #f #f '() '() x #f)))
-         (x (optimize! x e opts))
+         (x (optimize! (optimize! x e opts) e opts))
          (x (canonicalize! x))
-         (allocation (analyze-lexicals x)))
+         (special-vars (register-special-vars x))
+         (allocation (analyze-lexicals x special-vars)))
 
     (with-fluids ((*comp-module* e))
       (values (flatten-lambda x #f allocation)
diff --git a/module/language/tree-il/special.scm b/module/language/tree-il/special.scm
new file mode 100644
index 0000000..0e38084
--- /dev/null
+++ b/module/language/tree-il/special.scm
@@ -0,0 +1,104 @@
+(define-module (language tree-il special)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
+  #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
+  #:use-module (ice-9 vlist)
+  #:use-module (ice-9 match)
+  #:use-module (system base syntax)
+  #:use-module (system base message)
+  #:use-module (system vm program)
+  #:use-module (language tree-il)
+  #:use-module (system base pmatch)
+  #:export (register-special-vars))
+
+
+(define (register-special-vars x)
+  (define register (make-hash-table))
+  (let lp ((x x))
+    (record-case x
+      ((<application> proc args)                              
+       (record-case proc
+         ((<module-ref> src mod name public?)
+          (if (and (equal? mod '(system vm special-variable))
+                   (eq?    name 'special)
+                   (= (length args) 1))
+              (record-case (car args)
+                ((<lexical-ref> src gensym)
+                 (hashq-set! register gensym #t))
+                (else #t))))
+         (else #f))
+       (lp proc)
+       (for-each lp args))
+
+      ((<conditional> test consequent alternate)
+       (lp test)
+       (lp consequent)
+       (lp alternate))
+
+          
+      ((<lexical-set> gensym exp)
+       (lp exp))
+
+      ((<module-set> exp)
+       (lp exp))
+      
+      ((<toplevel-set> exp)
+       (lp exp))
+      
+      ((<toplevel-define> exp)
+       (lp exp))
+      
+      ((<sequence> exps)
+       (for-each lp exps))
+
+      ((<lambda> body)
+       (lp body))
+      
+      ((<lambda-case> opt kw inits gensyms body alternate)
+       (for-each lp inits)
+       (lp body)
+       (if alternate (lp alternate)))
+      
+      ((<let> gensyms vals body)
+       (for-each lp vals)
+       (lp body))
+
+      ((<letrec> gensyms vals body)
+       (for-each lp vals)
+       (lp body))
+      
+      ((<fix> gensyms vals body)
+       (for-each lp vals)
+       (lp body))
+
+      ((<let-values> exp body)
+       (lp exp)
+       (lp body))
+      
+      ((<dynwind> body winder unwinder)
+       (lp body)
+       (lp winder)
+       (lp unwinder))
+      
+      ((<dynlet> fluids vals body)
+       (lp body)
+       (for-each lp (append fluids vals)))
+          
+      ((<dynref> fluid)
+       (lp fluid))
+      
+      ((<dynset> fluid exp)
+       (lp fluid) (lp exp))
+      
+      ((<prompt> tag body handler)
+       (lp tag) 
+       (lp body) 
+       (lp handler))
+      
+      ((<abort> tag args tail)
+       (lp tag) (lp tail) (for-each lp args))
+      
+      (else #t)))
+  register)
+
diff --git a/module/system/vm/special-variable.scm b/module/system/vm/special-variable.scm
new file mode 100644
index 0000000..2068b84
--- /dev/null
+++ b/module/system/vm/special-variable.scm
@@ -0,0 +1,9 @@
+(define-module (system vm special-variable)
+  #:export (mark-as-special))
+
+(define special (lambda (x) #f))
+
+(define-syntax-rule (mark-as-special v ...)
+  (begin
+    (special v)
+    ...))

^ permalink raw reply related	[flat|nested] 13+ messages in thread
* Re: Special variables to relax boxing
@ 2013-03-23 10:30 Stefan Israelsson Tampe
  0 siblings, 0 replies; 13+ messages in thread
From: Stefan Israelsson Tampe @ 2013-03-23 10:30 UTC (permalink / raw)
  To: guile-devel

Hi guiler, Hi Daniel.

I lost this thread in my mail application, sorry, so I try to continue
this way instead.

* This feature for scheme is mainly for macro writers which knows what 
  they are doing and which only guard state varibles not seen to the 
  application programmer. This is how I use this feature in guile-log 
  so it will not change how most people need to reason about scheme, the 
beuty 
  remains. It makes insanly difficult tasks possible and simple things
  works as before.

* I really like guile to cater for advanced users. Not only
  noobs. Although we should hide this feature really deep and put some
  serious warnings in the documentatin about the use. I actually hate
  using set! and if you look at e.g. guile-syntax-parse you will see
  that I rewrote everything with let loops, e.g. I really prefere that
  way of looping. I do understand your worry to let people loose on
  writing set! heavy code - that is not scheme. I also ported common
  lisp code to scheme and It was a horrible experience to see all set!
  ing in the code and I just had to rewrite quite a lot of the common
  lisp with rewriting the loops. But then a new version comes out and
  you are smoked.

* If we are serious about combining emacs-lisp with guile we should
  really aim to have this feature added with deep support. The reason is 
that
  using set! in lisp world is a style of programming and is very
  common. For this reason I don't expect much emacs-lisp to combine
  well with undo redo additions via prompts. This hearts me really
  hard because it would be so cool to be able and schime in to a
  emacs-lisp project, add  a few lines of code and show of a well 
  functioning redo and undo addition to the complex emacs lisp code - 
  this would be a really good selling point for guile-emacs and as I
  said, I hate that we don't have a nice support for it.

* The proposed semantics piggy packs ontop of fluids sematics so
  everything we have done incorporating fluids carry over directly to
  special variables. And the extra stuff is really not that
  much. E.g. the maintainance burden is probably much less that your
  perception of it.

* We need 2 extra VM op's if we are serious ablut implementing this
  though but they are simply essentially a copy paste of the fluid
  code so one could factor out the code somewhat. Another possibility
  is to add an option to the current fluid vm ops.

* If you don't redo and undo nothing in the way scheme works changes
  it's only when you undo and redo stuff that the semantics becomes 
  important.

I really think that this is an important addition of guile and it would
really be a dissapointment not having it's support considering the
great gain you can get via optimizations with really not much extra
work in guile. Also I think that I managed to get the semantics and
implementation in a really beutiful state where a lot of effort have
been in making it mix well and be consistant in what it does and
actually get it into a state where it _is_ much simpler to reason
about the code considering other way's of implementing the 
optimisations.
Albait more difficult than before.

Have fun
/Stefan




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

end of thread, other threads:[~2013-03-23 18:31 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-19 22:05 Special variables to relax boxing Stefan Israelsson Tampe
2013-03-21  6:00 ` Mark H Weaver
2013-03-21  9:35   ` Stefan Israelsson Tampe
2013-03-21 15:35     ` Noah Lavine
2013-03-21 16:28       ` Stefan Israelsson Tampe
2013-03-21 19:03     ` Mark H Weaver
2013-03-21 20:15       ` Stefan Israelsson Tampe
2013-03-21 21:11         ` Noah Lavine
2013-03-22 22:33           ` Stefan Israelsson Tampe
2013-03-23  0:18             ` Daniel Hartwig
2013-03-23 15:34       ` Stefan Israelsson Tampe
2013-03-23 18:31         ` Stefan Israelsson Tampe
  -- strict thread matches above, loose matches on Subject: below --
2013-03-23 10:30 Stefan Israelsson Tampe

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).