shithub: femtolisp

Download patch

ref: 6ec69f537598e81cbb678ba5387c55c9274249e5
parent: 943c0d71d15c972ee730b417f0e47ef1f689efe6
author: JeffBezanson <[email protected]>
date: Wed Jun 10 20:34:50 EDT 2009

optimizations to map and case
several optimizations to the compiler itself, mostly involving
  emit and encode-byte-code
experimental, very basic profiling utility


--- a/femtolisp/compiler.lsp
+++ b/femtolisp/compiler.lsp
@@ -60,35 +60,38 @@
 	       (prog1 nconst
 		      (aset! b 2 (+ nconst 1)))))))
 (define (emit e inst . args)
-  (if (memq inst '(:loadv :loadg :setg))
-      (set! args (list (bcode:indexfor e (car args)))))
-  (let ((longform
-	 (assq inst '((:loadv :loadv.l) (:loadg :loadg.l) (:setg  :setg.l)
-		      (:loada :loada.l) (:seta  :seta.l)))))
-    (if (and longform
-	     (> (car args) 255))
-	(set! inst (cadr longform))))
-  (let ((longform
-	 (assq inst '((:loadc :loadc.l) (:setc :setc.l)))))
-    (if (and longform
-	     (or (> (car  args) 255)
-		 (> (cadr args) 255)))
-	(set! inst (cadr longform))))
-  (if (eq? inst :loada)
-      (cond ((equal? args '(0))
-	     (set! inst :loada0)
-	     (set! args ()))
-	    ((equal? args '(1))
-	     (set! inst :loada1)
-	     (set! args ()))))
-  (if (eq? inst :loadc)
-      (cond ((equal? args '(0 0))
-	     (set! inst :loadc00)
-	     (set! args ()))
-	    ((equal? args '(0 1))
-	     (set! inst :loadc01)
-	     (set! args ()))))
-  (aset! e 0 (nreconc (cons inst args) (aref e 0)))
+  (if (null? args)
+      (aset! e 0 (cons inst (aref e 0)))
+      (begin
+	(if (memq inst '(:loadv :loadg :setg))
+	    (set! args (list (bcode:indexfor e (car args)))))
+	(let ((longform
+	       (assq inst '((:loadv :loadv.l) (:loadg :loadg.l) (:setg :setg.l)
+			    (:loada :loada.l) (:seta  :seta.l)))))
+	  (if (and longform
+		   (> (car args) 255))
+	      (set! inst (cadr longform))))
+	(let ((longform
+	       (assq inst '((:loadc :loadc.l) (:setc :setc.l)))))
+	  (if (and longform
+		   (or (> (car  args) 255)
+		       (> (cadr args) 255)))
+	      (set! inst (cadr longform))))
+	(if (eq? inst :loada)
+	    (cond ((equal? args '(0))
+		   (set! inst :loada0)
+		   (set! args ()))
+		  ((equal? args '(1))
+		   (set! inst :loada1)
+		   (set! args ()))))
+	(if (eq? inst :loadc)
+	    (cond ((equal? args '(0 0))
+		   (set! inst :loadc00)
+		   (set! args ()))
+		  ((equal? args '(0 1))
+		   (set! inst :loadc01)
+		   (set! args ()))))
+	(aset! e 0 (nreconc (cons inst args) (aref e 0)))))
   e)
 
 (define (make-label e)   (gensym))
@@ -99,8 +102,11 @@
 (define (encode-byte-code e)
   (let* ((cl (reverse! e))
 	 (v  (list->vector cl))
-	 (long? (>= (+ (length v)
-		       (* 3 (count (lambda (i)
+	 (long? (>= (+ (length v)  ; 1 byte for each entry, plus...
+		       ; at most half the entries in this vector can be
+		       ; instructions accepting 32-bit arguments
+		       (* 3 (div0 (length v) 2))
+		       #;(* 3 (count (lambda (i)
 				     (memq i '(:loadv.l :loadg.l :setg.l
 					       :loada.l :seta.l :loadc.l
 					       :setc.l :jmp :brt :brf
@@ -112,7 +118,8 @@
 	  (label-to-loc   (table))
 	  (fixup-to-label (table))
 	  (bcode          (buffer))
-	  (vi             #f))
+	  (vi             #f)
+	  (nxt            #f))
       (while (< i n)
 	(begin
 	  (set! vi (aref v i))
@@ -123,46 +130,44 @@
 		(io.write bcode
 			  (byte
 			   (get Instructions
-				(if (and long?
-					 (memq vi '(:jmp :brt :brf)))
+				(if long?
 				    (case vi
 				      (:jmp :jmp.l)
 				      (:brt :brt.l)
-				      (:brf :brf.l))
+				      (:brf :brf.l)
+				      (else vi))
 				    vi))))
 		(set! i (+ i 1))
-		(if (< i n)
-		    (let ((nxt (aref v i)))
-		      (case vi
-			((:loadv.l :loadg.l :setg.l :loada.l :seta.l :largc
-			  :lvargc)
-			 (io.write bcode (uint32 nxt))
-			 (set! i (+ i 1)))
-			
-			((:loada :seta :call :tcall :loadv :loadg :setg
-			  :list :+ :- :* :/ :vector :argc :vargc :loadi8
-			  :apply :tapply)
-			 (io.write bcode (uint8 nxt))
-			 (set! i (+ i 1)))
-			
-			((:loadc :setc)  ; 2 uint8 args
-			 (io.write bcode (uint8 nxt))
-			 (set! i (+ i 1))
-			 (io.write bcode (uint8 (aref v i)))
-			 (set! i (+ i 1)))
+		(set! nxt (if (< i n) (aref v i) #f))
+		(cond ((memq vi '(:jmp :brf :brt))
+		       (put! fixup-to-label (sizeof bcode) nxt)
+		       (io.write bcode ((if long? uint32 uint16) 0))
+		       (set! i (+ i 1)))
+		      ((number? nxt)
+		       (case vi
+			 ((:loadv.l :loadg.l :setg.l :loada.l :seta.l
+			   :largc :lvargc)
+			  (io.write bcode (uint32 nxt))
+			  (set! i (+ i 1)))
+			 
+			 ((:loadc :setc)  ; 2 uint8 args
+			  (io.write bcode (uint8 nxt))
+			  (set! i (+ i 1))
+			  (io.write bcode (uint8 (aref v i)))
+			  (set! i (+ i 1)))
+			 
+			 ((:loadc.l :setc.l)  ; 2 uint32 args
+			  (io.write bcode (uint32 nxt))
+			  (set! i (+ i 1))
+			  (io.write bcode (uint32 (aref v i)))
+			  (set! i (+ i 1)))
+			 
+			 (else
+			  ; other number arguments are always uint8
+			  (io.write bcode (uint8 nxt))
+			  (set! i (+ i 1)))))
+		      (else #f))))))
 
-			((:loadc.l :setc.l)  ; 2 uint32 args
-			 (io.write bcode (uint32 nxt))
-			 (set! i (+ i 1))
-			 (io.write bcode (uint32 (aref v i)))
-			 (set! i (+ i 1)))
-			
-			((:jmp :brf :brt)
-			 (put! fixup-to-label (sizeof bcode) nxt)
-			 (io.write bcode ((if long? uint32 uint16) 0))
-			 (set! i (+ i 1)))
-			
-			(else #f))))))))
       (table.foreach
        (lambda (addr labl)
 	 (begin (io.seek bcode addr)
--- a/femtolisp/flisp.boot
+++ b/femtolisp/flisp.boot
@@ -105,7 +105,7 @@
 map!
 #function("r2\x7f^\x7fF6I02\x7f~\x7fM31O2\x7fNm15402;" [])
 map
-#function("r2\x7f?690\x7f;~\x7fM31e0~\x7fN32K;" [map])
+#function("r2c0__L1u43;" [#function("v\x7fm02^\x81F6Q02\x7f\x80\x81M31_KPNm12\x81No015602~N;" [])])
 make-system-image
 #function("r1c0e1~e2e3e434c5e6u44;" [#function("v^k02c1c2qu42;" [*print-pretty* #function("vc0qc1qt~302;" [#function("r0e0c1qe2e3e430313142;" [for-each #function("r1~E16h02e0~31@16h02e1~31G@16h02e2~i1132@16h02e3e1~3131@6\x950e4i10~322e5i10c6322e4i10e1~31322e5i10c642;^;" [constant? top-level-value memq iostream? io.print io.write "\n"]) reverse! simple-sort environment]) #function("r1\x80302e0~41;" [raise])]) #function("r0e0\x80312i02k1;" [io.close *print-pretty*])]) file :write :create :truncate (*linefeed* *directory-separator* *argv* that *print-pretty* *print-width* *print-readably*) *print-pretty*])
 make-label
@@ -181,15 +181,15 @@
 error
 #function("s0e0c1~K41;" [raise error])
 encode-byte-code
-#function("r1c0e1~31u42;" [#function("vc0e1~31u42;" [#function("vc0e1e2~31b3e3c4q\x8032T2yc532u42;" [#function("vc0e1\x8031`e230e230e330^u47;" [#function("v^\x7f~X6\xb102i10\x7f[m52g5e0<6f0e1g2i10\x7fay[e2g431332\x7fb2ym15\xae0e3g4e4e5e6\x8016~02e7g5c8326\x8b0c9g5u325\x8d0g53231322\x7faym12\x7f~X6\xad0c:i10\x7f[u325\xae0^5202e;c<qg3322e=g441;" [:label put! sizeof io.write byte get Instructions memq (:jmp :brt :brf) #function("v~e0=6;0e1;~e2=6E0e3;~e4=6O0e5;^;" [:jmp :jmp.l :brt :brt.l :brf :brf.l]) #function("vc0i05u42;" [#function("ve0~c1326Q0e2i14e3\x8031322i11ayo11;e0~c4326q0e2i14e5\x8031322i11ayo11;e0~c6326\xad0e2i14e5\x8031322i11ayo112e2i14e5i30i11[31322i11ayo11;e0~c7326\xe90e2i14e3\x8031322i11ayo112e2i14e3i30i11[31322i11ayo11;e0~c8326$0e9i13e:i1431\x80332e2i14i206\x130e35\x150e;`31322i11ayo11;^;" [memv (:loadv.l :loadg.l :setg.l :loada.l :seta.l :largc :lvargc) io.write uint32 (:loada :seta :call :tcall :loadv :loadg :setg :list :+ :- :* :/ :vector :argc :vargc :loadi8 :apply :tapply) uint8 (:loadc :setc) (:loadc.l :setc.l) (:jmp :brf :brt) put! sizeof uint16])]) table.foreach #function("r2e0i04~322e1i04i106K0e25M0e3e4i02\x7f323142;" [io.seek io.write uint32 uint16 get]) io.tostring!]) length table buffer]) >= length count #function("r1e0~c142;" [memq (:loadv.l :loadg.l :setg.l :loada.l :seta.l :loadc.l :setc.l :jmp :brt :brf :largc :lvargc)]) 65536]) list->vector]) reverse!])
+#function("r1c0e1~31u42;" [#function("vc0e1~31u42;" [#function("vc0e1e2~31b3e2~31b2VT2yc332u42;" [#function("vc0e1\x8031`e230e230e330^^u48;" [#function("v^\x7f~X6\xea02i10\x7f[m52g5e0<6f0e1g2i10\x7fay[e2g431332\x7fb2ym15\xe70e3g4e4e5e6\x806~0c7g5u325\x800g53231322\x7faym12\x7f~X6\x9b0i10\x7f[5\x9c0^m62e8g5c9326\xd60e1g3e2g431g6332e3g4\x806\xc60e:5\xc80e;`31322\x7faym15\xe70g6D6\xe60c<g5u325\xe70^5202e=c>qg3322e?g441;" [:label put! sizeof io.write byte get Instructions #function("v~e0<6;0e1;~e2<6E0e3;~e4<6O0e5;i05;" [:jmp :jmp.l :brt :brt.l :brf :brf.l]) memq (:jmp :brf :brt) uint32 uint16 #function("ve0~c1326Q0e2i04e3i0631322\x81ayo01;e0~c4326\x890e2i04e5i0631322\x81ayo012e2i04e5i20\x81[31322\x81ayo01;e0~c6326\xc10e2i04e3i0631322\x81ayo012e2i04e3i20\x81[31322\x81ayo01;e2i04e5i0631322\x81ayo01;" [memq (:loadv.l :loadg.l :setg.l :loada.l :seta.l :largc :lvargc) io.write uint32 (:loadc :setc) uint8 (:loadc.l :setc.l)]) table.foreach #function("r2e0i04~322e1i04i106K0e25M0e3e4i02\x7f323142;" [io.seek io.write uint32 uint16 get]) io.tostring!]) length table buffer]) >= length 65536]) list->vector]) reverse!])
 emit
-#function("s2e0\x7fc1326K0e2~g2M32L1m25L0^2c3e4\x7fc532u322c6e4\x7fc732u322\x7fe8<6\x980g2c9>6\x810e:m12_m25\x950g2c;>6\x940e<m12_m25\x950^5\x990^2\x7fe=<6\xcb0g2c>>6\xb40e?m12_m25\xc80g2c@>6\xc70eAm12_m25\xc80^5\xcc0^2~`eB\x7fg2K~`[32\\2~;" [memq (:loadv :loadg :setg) bcode:indexfor #function("v~16A02e0i02Mc1326M0e2~31o01;^;" [> 255 cadr]) assq ((:loadv :loadv.l) (:loadg :loadg.l) (:setg :setg.l) (:loada :loada.l) (:seta :seta.l)) #function("v~16S02e0i02Mc13217S02e0e2i0231c1326_0e2~31o01;^;" [> 255 cadr]) ((:loadc :loadc.l) (:setc :setc.l)) :loada (0) :loada0 (1) :loada1 :loadc (0 0) :loadc00 (0 1) :loadc01 nreconc])
+#function("s2g2A6C0~`\x7f~`[K\\5\xec0e0\x7fc1326\\0e2~g2M32L1m25]0^2c3e4\x7fc532u322c6e4\x7fc732u322\x7fe8<6\xa90g2c9>6\x920e:m12_m25\xa60g2c;>6\xa50e<m12_m25\xa60^5\xaa0^2\x7fe=<6\xdc0g2c>>6\xc50e?m12_m25\xd90g2c@>6\xd80eAm12_m25\xd90^5\xdd0^2~`eB\x7fg2K~`[32\\2~;" [memq (:loadv :loadg :setg) bcode:indexfor #function("v~16A02e0i02Mc1326M0e2~31o01;^;" [> 255 cadr]) assq ((:loadv :loadv.l) (:loadg :loadg.l) (:setg :setg.l) (:loada :loada.l) (:seta :seta.l)) #function("v~16S02e0i02Mc13217S02e0e2i0231c1326_0e2~31o01;^;" [> 255 cadr]) ((:loadc :loadc.l) (:setc :setc.l)) :loada (0) :loada0 (1) :loada1 :loadc (0 0) :loadc00 (0 1) :loadc01 nreconc])
 div
 #function("r2~\x7fV~`X16M02\x7f`X16F02a17M02b/17S02`y;" [])
 display
 #function("r1e0~312];" [princ])
 disassemble
-#function("s1\x7fA6H0e0~`322e1302];5I0^2c2\x7fMe3~31e4~31u44;" [disassemble newline #function("vc0^u42;" [#function("vc0qm02c1`e2\x8131u43;" [#function("r1~J16<02~G@6Q0e0c1312e2~i10ay42;e3~41;" [princ "\n" disassemble print]) #function("v^~\x7fX6K02c0e1c2q^e333u32520;" [#function("ve0\x80`326A0e1305B0^2`i20azc2qw2e3e4\x8031c5e6e7~31a32c8342\x80ayo002c9~u42;" [> newline #function("r1e0c141;" [princ "\t"]) princ hex5 ":  " string.tail string "\t" #function("ve0~c1326Y0i20i32e2i31i1032[312i10b4yo10;e0~c3326}0i20i32i31i10[[312i10ayo10;e0~c4326\xa00e5e6i31i10[31312i10ayo10;e0~c7326\xc70e5e6e2i31i103231312i10b4yo10;e0~c8326\x050e5e6i31i10[31c9322i10ayo102e5e6i31i10[31312i10ayo10;e0~c:326K1e5e6e2i31i103231c9322i10b4yo102e5e6e2i31i103231312i10b4yo10;e0~c;326t1e5c<e=e>i31i103231322i10b2yo10;e0~c?326\x9d1e5c<e=e2i31i103231322i10b4yo10;^;" [memv (:loadv.l :loadg.l :setg.l) ref-uint32-LE (:loadv :loadg :setg) (:loada :seta :call :tcall :list :+ :- :* :/ :vector :argc :vargc :loadi8 :apply :tapply) princ number->string (:loada.l :seta.l :largc :lvargc) (:loadc :setc) " " (:loadc.l :setc.l) (:jmp :brf :brt) "@" hex5 ref-uint16-LE (:jmp.l :brf.l :brt.l)])]) table.foldl #function("r3g217F02\x7fi21\x80[<16F02~;" []) Instructions]) length])]) function:code function:vals])
+#function("s1\x7fA6H0e0~`322e1302];5I0^2c2\x7fMe3~31e4~31u44;" [disassemble newline #function("vc0^u42;" [#function("vc0qm02c1`e2\x8131u43;" [#function("r1~J16<02~G@6Q0e0c1312e2~i10ay42;e3~41;" [princ "\n" disassemble print]) #function("v^~\x7fX6K02c0e1c2q^e333u32520;" [#function("ve0\x80`326A0e1305B0^2`i20azc2qw2e3e4\x8031c5e6e7~31a32c8342\x80ayo002c9~u42;" [> newline #function("r1e0c141;" [princ "\t"]) princ hex5 ":  " string.tail string "\t" #function("ve0~c1326Y0i20i32e2i31i1032[312i10b4yo10;e0~c3326}0i20i32i31i10[[312i10ayo10;e0~c4326\xa00e5e6i31i10[31312i10ayo10;e0~c7326\xc70e5e6e2i31i103231312i10b4yo10;e0~c8326\x050e5e6i31i10[31c9322i10ayo102e5e6i31i10[31312i10ayo10;e0~c:326K1e5e6e2i31i103231c9322i10b4yo102e5e6e2i31i103231312i10b4yo10;e0~c;326t1e5c<e=e>i31i103231322i10b2yo10;e0~c?326\x9d1e5c<e=e2i31i103231322i10b4yo10;^;" [memq (:loadv.l :loadg.l :setg.l) ref-uint32-LE (:loadv :loadg :setg) (:loada :seta :call :tcall :list :+ :- :* :/ :vector :argc :vargc :loadi8 :apply :tapply) princ number->string (:loada.l :seta.l :largc :lvargc) (:loadc :setc) " " (:loadc.l :setc.l) (:jmp :brf :brt) "@" hex5 ref-uint16-LE (:jmp.l :brf.l :brt.l)])]) table.foldl #function("r3g217F02\x7fi21\x80[<16F02~;" []) Instructions]) length])]) function:code function:vals])
 delete-duplicates
 #function("r1~?690~;c0~M~Nu43;" [#function("ve0~\x7f326@0e1\x7f41;~e1\x7f31K;" [member delete-duplicates])])
 count
@@ -203,7 +203,7 @@
 compile-thunk
 #function("r1e0c1_~L341;" [compile lambda])
 compile-sym
-#function("r4c0e1g2\x7f`]34u42;" [#function("vc0~Mu42;" [#function("v~c0=6J0e1i10i13`[e2\x803143;~c3=6h0e1i10i13a[e2\x8031e4\x803144;e1i10i13b2[i1243;" [arg emit cadr closed caddr])]) lookup-sym])
+#function("r4c0e1g2\x7f`]34u42;" [#function("vc0~Mu42;" [#function("v~c0<6J0e1i10i13`[e2\x803143;~c3<6h0e1i10i13a[e2\x8031e4\x803144;e1i10i13b2[i1243;" [arg emit cadr closed caddr])]) lookup-sym])
 compile-short-circuit
 #function("r6g3?6C0e0~\x7fg2g444;g3N?6V0e0~\x7fg2g3M44;c1e2~31u42;" [compile-in #function("ve0\x80\x81^i03M342e1\x80e2322e1\x80i05~332e1\x80e3322e4\x80\x81i02i03Ni04i05362e5\x80~42;" [compile-in emit :dup :pop compile-short-circuit mark-label]) make-label])
 compile-prog1
@@ -213,7 +213,7 @@
 compile-let
 #function("r4c0g3Mg3Nu43;" [#function("ve0\x7fe1e2~3131326F0^5Q0e3e4c5~32312e6\x80e7e8\x81~]33332c9e:\x80\x81\x7f33u42;" [length= length cadr error string "apply: incorrect number of arguments to " emit :loadv compile-f #function("ve0i10e1322e0i10i126K0e25M0e3a~y43;" [emit :copyenv :tcall :call]) compile-arglist])])
 compile-in
-#function("r4g3C6C0e0~\x7fg3c144;g3?6\xb10g3`<6X0e2~e342;g3a<6g0e2~e442;g3]<6v0e2~e542;g3^<6\x850e2~e642;g3_<6\x940e2~e742;e8g3316\xa70e2~e9g343;e2~e:g343;c;g3Mu42;" [compile-sym [:loada :loadc :loadg] emit :load0 :load1 :loadt :loadf :loadnil fits-i8 :loadi8 :loadv #function("v~c0=6G0e1\x80e2e3i033143;~c4=6[0e5\x80\x81i02i0344;~c6=6p0e7\x80\x81i02i03N44;~c8=6\x810e9\x80\x81i0343;~c:=6\xa00e1\x80e2e;\x81i0332332e1\x80e<42;~c==6\xb50e>\x80\x81i02i03N44;~c?=6\xca0e@\x80\x81i02i03N44;~cA=6\xe90eB\x80\x81e3i0331c6eCi0331K44;~cD=6\x0c0eE\x80\x81e3i0331eFi0331eGi033145;~cH=6*0eI\x80\x81]e3i0331342e1\x80eJ42;~cK=6P1eI\x80\x81^eFi0331342eL\x80\x81e3i0331cM44;~cN=6\x9b1eI\x80\x81^c:_e3i0331L3342eOeFi0331316}1^5\x831ePcQ312eI\x80\x81^eFi0331342e1\x80eR42;eS\x80\x81i02i0344;" [quote emit :loadv cadr if compile-if begin compile-begin prog1 compile-prog1 lambda compile-f :closure and compile-and or compile-or while compile-while cddr for compile-for caddr cadddr return compile-in :ret set! compile-sym [:seta :setc :setg] trycatch 1arg-lambda? error "trycatch: second form must be a 1-argument lambda" :trycatch compile-app])])
+#function("r4g3C6C0e0~\x7fg3c144;g3?6\xb10g3`<6X0e2~e342;g3a<6g0e2~e442;g3]<6v0e2~e542;g3^<6\x850e2~e642;g3_<6\x940e2~e742;e8g3316\xa70e2~e9g343;e2~e:g343;c;g3Mu42;" [compile-sym [:loada :loadc :loadg] emit :load0 :load1 :loadt :loadf :loadnil fits-i8 :loadi8 :loadv #function("v~c0<6G0e1\x80e2e3i033143;~c4<6[0e5\x80\x81i02i0344;~c6<6p0e7\x80\x81i02i03N44;~c8<6\x810e9\x80\x81i0343;~c:<6\xa00e1\x80e2e;\x81i0332332e1\x80e<42;~c=<6\xb50e>\x80\x81i02i03N44;~c?<6\xca0e@\x80\x81i02i03N44;~cA<6\xe90eB\x80\x81e3i0331c6eCi0331K44;~cD<6\x0c0eE\x80\x81e3i0331eFi0331eGi033145;~cH<6*0eI\x80\x81]e3i0331342e1\x80eJ42;~cK<6P1eI\x80\x81^eFi0331342eL\x80\x81e3i0331cM44;~cN<6\x9b1eI\x80\x81^c:_e3i0331L3342eOeFi0331316}1^5\x831ePcQ312eI\x80\x81^eFi0331342e1\x80eR42;eS\x80\x81i02i0344;" [quote emit :loadv cadr if compile-if begin compile-begin prog1 compile-prog1 lambda compile-f :closure and compile-and or compile-or while compile-while cddr for compile-for caddr cadddr return compile-in :ret set! compile-sym [:seta :setc :setg] trycatch 1arg-lambda? error "trycatch: second form must be a 1-argument lambda" :trycatch compile-app])])
 compile-if
 #function("r4c0e1~31e1~31e2g331e3g331e4g331F6]0e5g3315^0^u46;" [#function("vg2]<6D0e0\x80\x81i02g344;g2^<6W0e0\x80\x81i02g444;e0\x80\x81^g2342e1\x80e2~332e0\x80\x81i02g3342i026\x860e1\x80e3325\x8e0e1\x80e4\x7f332e5\x80~322e0\x80\x81i02g4342e5\x80\x7f42;" [compile-in emit :brf :ret :jmp mark-label]) make-label cadr caddr cdddr cadddr])
 compile-for
@@ -221,7 +221,7 @@
 compile-f
 #function("s2c0e130e2\x7f31u43;" [#function("vi02A@6C0e0~e1325\x970e2\x7fe3326j0e0~e4\x7f31A6^0e55`0e6e7\x7f31335\x970e4\x7f31A6\x820e0~e8e7\x7f31335\x970e0~e9\x7f?6\x900`5\x950e7\x7f31332e:~e;\x7f31\x80K]e<\x8131342e0~e=322e>e?e@~3131eA~3142;" [emit :let length> MAX_ARGS lastcdr :largc :lvargc length :argc :vargc compile-in to-proper caddr :ret function encode-byte-code bcode:code const-to-idx-vec]) make-code-emitter cadr])
 compile-call
-#function("r4c0g3Mu42;" [#function("vc0~C16]02e1~\x8132@16]02~E16]02e2~3116]02e3~31G6h0e3~315i0~u42;" [#function("vc0~G16?02e1~31u42;" [#function("v~@6E0e0i20i21^\x80345F0^2c1e2i20i21i23N33u42;" [compile-in #function("v\x806C0c0e1e2\x80^33u42;e3i30i326S0e45U0e5~43;" [#function("v~16A02e0i43N~32@6O0e1i20~325P0^2c2i10u42;" [length= argc-error #function("v~e0=6X0i10`W6J0e1i50e242;e1i50i20i1043;~e3=6\x920i10`W6q0e1i50e442;i10b2W6\x840e1i50e542;e1i50i20i1043;~e6=6\xdd0i10`W6\xaa0e7i30a42;i10aW6\xbc0e1i50e842;i10b2W6\xcf0e1i50e942;e1i50i20i1043;~e:=6\x040i10`W6\xf60e1i50e;42;e1i50i20i1043;~e<=6*0i10`W6\x1c0e7i30a42;e1i50i20i1043;~e==6S1i10`W6E1e1i50e>c?43;e1i50i20i1043;~e@=6\x851i10b2X6m1e7i30b242;e1i50i526}1eA5\x7f1e@i1043;e1i50i2042;" [:list emit :loadnil :+ :load0 :add2 :- argc-error :neg :sub2 :* :load1 :/ :vector :loadv [] :apply :tapply])]) get arg-counts emit :tcall :call]) compile-arglist]) builtin->instruction]) in-env? constant? top-level-value])])
+#function("r4c0g3Mu42;" [#function("vc0~C16]02e1~\x8132@16]02~E16]02e2~3116]02e3~31G6h0e3~315i0~u42;" [#function("vc0~G16?02e1~31u42;" [#function("v~@6E0e0i20i21^\x80345F0^2c1e2i20i21i23N33u42;" [compile-in #function("v\x806C0c0e1e2\x80^33u42;e3i30i326S0e45U0e5~43;" [#function("v~16A02e0i43N~32@6O0e1i20~325P0^2c2i10u42;" [length= argc-error #function("v~e0<6X0i10`W6J0e1i50e242;e1i50i20i1043;~e3<6\x920i10`W6q0e1i50e442;i10b2W6\x840e1i50e542;e1i50i20i1043;~e6<6\xdd0i10`W6\xaa0e7i30a42;i10aW6\xbc0e1i50e842;i10b2W6\xcf0e1i50e942;e1i50i20i1043;~e:<6\x040i10`W6\xf60e1i50e;42;e1i50i20i1043;~e<<6*0i10`W6\x1c0e7i30a42;e1i50i20i1043;~e=<6S1i10`W6E1e1i50e>c?43;e1i50i20i1043;~e@<6\x851i10b2X6m1e7i30b242;e1i50i526}1eA5\x7f1e@i1043;e1i50i2042;" [:list emit :loadnil :+ :load0 :add2 :- argc-error :neg :sub2 :* :load1 :/ :vector :loadv [] :apply :tapply])]) get arg-counts emit :tcall :call]) compile-arglist]) builtin->instruction]) in-env? constant? top-level-value])])
 compile-begin
 #function("r4g3?6B0e0~\x7fg2^44;g3N?6U0e0~\x7fg2g3M44;e0~\x7f^g3M342e1~e2322e3~\x7fg2g3N44;" [compile-in emit :pop compile-begin])
 compile-arglist
@@ -319,6 +319,6 @@
 *whitespace*
 "\t\n\v\f\r \u0085  ᠎           \u2028\u2029   "
 *syntax-environment*
-#table(define #function("s1~C6?0c0~\x7fML3;c0~Mc1~N\x7fKKL3;" [set! lambda])  letrec #function("s1c0e1e2~32e3e1c4q~32\x7f32KKe1c5q~32K;" [lambda map car nconc #function("r1c0~K;" [set!]) #function("r1^;" [])])  backquote #function("r1e0~41;" [bq-process])  assert #function("r1c0~]c1c2c3~L2L2L2L4;" [if raise quote assert-failed])  label #function("r2c0~L1c1~\x7fL3L3^L2;" [lambda set!])  do #function("s2c0e130\x7fMe2e3~32e2e4~32e2c5q~32u46;" [#function("vc0~c1g2c2\x7fe3c4L1e5\x81N3132e3c4L1e5i0231e3~L1e5g43132L133L4L3L2L1e3~L1e5g33132L3;" [letrec lambda if nconc begin copy-list]) gensym map car cadr #function("r1e0~31F6A0e1~41;~M;" [cddr caddr])])  when #function("s1c0~c1\x7fK^L4;" [if begin])  unwind-protect #function("r2c0e130e130u43;" [#function("vc0\x7fc1_\x81L3L2L1c2c3\x80c1~L1c4\x7fL1c5~L2L3L3L3\x7fL1L3L3;" [let lambda prog1 trycatch begin raise]) gensym])  dotimes #function("s1c0~Me1~31u43;" [#function("vc0`c1\x7faL3e2c3L1~L1L1e4\x813133L4;" [for - nconc lambda copy-list]) cadr])  define-macro #function("s1c0c1~ML2c2~N\x7fKKL3;" [set-syntax! quote lambda])  unless #function("s1c0~^c1\x7fKL4;" [if begin])  let #function("s1c0^u42;" [#function("v\x80C6H0\x80m02\x81Mo002\x81No015I0^2c0c1e2c3q\x8032\x81KKe2c4q\x8032u43;" [#function("v\x806>0c0\x80~L35?0~\x7fK;" [label]) lambda map #function("r1~F6:0~M;~;" []) #function("r1~F6=0e0~41;^;" [cadr])])])  cond #function("s0c0^u42;" [#function("vc0qm02~\x8041;" [#function("r1~?690^;c0~Mu42;" [#function("v~Mc0<17?02~M]<6Q0~NA6K0~M;c1~NK;~NA6e0c2~Mi10\x80N31L3;c3~Mc1~NKi10\x80N31L4;" [else begin or if])])])])  throw #function("r2c0c1c2c3L2~\x7fL4L2;" [raise list quote thrown-value])  time #function("r1c0e130u42;" [#function("vc0~c1L1L2L1c2\x80c3c4c5c1L1~L3c6L4L3L3;" [let time.now prog1 princ "Elapsed time: " - " seconds\n"]) gensym])  let* #function("s1~?6J0e0c1L1_L1e2\x7f3133L1;e0c1L1e3~31L1L1e2~NF6w0e0c4L1~NL1e2\x7f3133L15x0\x7f3133e5~31L2;" [nconc lambda copy-list caar let* cadar])  case #function("s1c0^u42;" [#function("vc0qm02c1e230u42;" [#function("r2\x7fc0<6<0c0;\x7fA6C0^;\x7f?6S0c1~e2\x7f31L3;\x7fNA6e0c1~e2\x7fM31L3;c3~c4\x7fL2L3;" [else eqv? quote-value memv quote]) #function("vc0~i10L2L1e1c2L1e3e4c5qi11323132L3;" [let nconc cond copy-list map #function("r1i10\x80~M32~NK;" [])]) gensym])])  catch #function("r2c0e130u42;" [#function("vc0\x81c1~L1c2c3c4~L2c5c6~L2c7c8L2L3c5c9~L2\x80L3L4c:~L2c;~L2L4L3L3;" [trycatch lambda if and pair? eq car quote thrown-value cadr caddr raise]) gensym]))
+#table(define #function("s1~C6?0c0~\x7fML3;c0~Mc1~N\x7fKKL3;" [set! lambda])  letrec #function("s1c0e1e2~32e3e1c4q~32\x7f32KKe1c5q~32K;" [lambda map car nconc #function("r1c0~K;" [set!]) #function("r1^;" [])])  backquote #function("r1e0~41;" [bq-process])  assert #function("r1c0~]c1c2c3~L2L2L2L4;" [if raise quote assert-failed])  label #function("r2c0~L1c1~\x7fL3L3^L2;" [lambda set!])  do #function("s2c0e130\x7fMe2e3~32e2e4~32e2c5q~32u46;" [#function("vc0~c1g2c2\x7fe3c4L1e5\x81N3132e3c4L1e5i0231e3~L1e5g43132L133L4L3L2L1e3~L1e5g33132L3;" [letrec lambda if nconc begin copy-list]) gensym map car cadr #function("r1e0~31F6A0e1~41;~M;" [cddr caddr])])  when #function("s1c0~c1\x7fK^L4;" [if begin])  unwind-protect #function("r2c0e130e130u43;" [#function("vc0\x7fc1_\x81L3L2L1c2c3\x80c1~L1c4\x7fL1c5~L2L3L3L3\x7fL1L3L3;" [let lambda prog1 trycatch begin raise]) gensym])  dotimes #function("s1c0~Me1~31u43;" [#function("vc0`c1\x7faL3e2c3L1~L1L1e4\x813133L4;" [for - nconc lambda copy-list]) cadr])  define-macro #function("s1c0c1~ML2c2~N\x7fKKL3;" [set-syntax! quote lambda])  unless #function("s1c0~^c1\x7fKL4;" [if begin])  let #function("s1c0^u42;" [#function("v\x80C6H0\x80m02\x81Mo002\x81No015I0^2c0c1e2c3q\x8032\x81KKe2c4q\x8032u43;" [#function("v\x806>0c0\x80~L35?0~\x7fK;" [label]) lambda map #function("r1~F6:0~M;~;" []) #function("r1~F6=0e0~41;^;" [cadr])])])  cond #function("s0c0^u42;" [#function("vc0qm02~\x8041;" [#function("r1~?690^;c0~Mu42;" [#function("v~Mc0<17?02~M]<6Q0~NA6K0~M;c1~NK;~NA6e0c2~Mi10\x80N31L3;c3~Mc1~NKi10\x80N31L4;" [else begin or if])])])])  throw #function("r2c0c1c2c3L2~\x7fL4L2;" [raise list quote thrown-value])  time #function("r1c0e130u42;" [#function("vc0~c1L1L2L1c2\x80c3c4c5c1L1~L3c6L4L3L3;" [let time.now prog1 princ "Elapsed time: " - " seconds\n"]) gensym])  let* #function("s1~?6J0e0c1L1_L1e2\x7f3133L1;e0c1L1e3~31L1L1e2~NF6w0e0c4L1~NL1e2\x7f3133L15x0\x7f3133e5~31L2;" [nconc lambda copy-list caar let* cadar])  case #function("s1c0^u42;" [#function("vc0qm02c1e230u42;" [#function("r2\x7fc0<6<0c0;\x7fA6C0^;\x7fC6S0c1~e2\x7f31L3;\x7f?6c0c3~e2\x7f31L3;\x7fNA6u0c3~e2\x7fM31L3;e4e5\x7f326\x8a0c6~c7\x7fL2L3;c8~c7\x7fL2L3;" [else eq? quote-value eqv? every symbol? memq quote memv]) #function("vc0~i10L2L1e1c2L1e3e4c5qi11323132L3;" [let nconc cond copy-list map #function("r1i10\x80~M32~NK;" [])]) gensym])])  catch #function("r2c0e130u42;" [#function("vc0\x81c1~L1c2c3c4~L2c5c6~L2c7c8L2L3c5c9~L2\x80L3L4c:~L2c;~L2L4L3L3;" [trycatch lambda if and pair? eq car quote thrown-value cadr caddr raise]) gensym]))
 *banner*
 ";  _\n; |_ _ _ |_ _ |  . _ _\n; | (-||||_(_)|__|_)|_)\n;-------------------|----------------------------------------------------------\n\n"
--- a/femtolisp/flisp.c
+++ b/femtolisp/flisp.c
@@ -799,7 +799,7 @@
 #endif
 
 /*
-  stack on entry: <func>  <args...>
+  stack on entry: <func>  <up to MAX_ARGS args...>  <arglist if nargs>MAX_ARGS>
   caller's responsibility:
   - put the stack in this state
   - provide arg count
@@ -819,7 +819,7 @@
     uint8_t *code;
 
     // temporary variables (not necessary to preserve across calls)
-    uint32_t op;
+    uint8_t op;
     symbol_t *sym;
     static cons_t *c;
     static value_t *pv;
@@ -935,9 +935,10 @@
                     v = apply_cl(n);
                 }
                 else {
-                    op = uintval(func);
-                    if (op > OP_ASET)
+                    i = uintval(func);
+                    if (i > OP_ASET)
                         type_error("apply", "function", func);
+                    op = (uint8_t)i;
                     s = builtin_arg_counts[op];
                     if (s >= 0)
                         argcount(builtin_names[op], n, s);
--- a/femtolisp/system.lsp
+++ b/femtolisp/system.lsp
@@ -21,8 +21,15 @@
 (define (symbol-syntax s) (get *syntax-environment* s #f))
 
 (define (map f lst)
-  (if (atom? lst) lst
-      (cons (f (car lst)) (map f (cdr lst)))))
+  ((lambda (first acc)
+     (begin
+       (set! first acc)
+       (while (pair? lst)
+	      (begin (set! acc
+			   (cdr (set-cdr! acc (cons (f (car lst)) ()))))
+		     (set! lst (cdr lst))))
+       (cdr first)))
+   () (list ())))
 
 (define-macro (label name fn)
   (list (list 'lambda (list name) (list 'set! name fn)) #f))
@@ -362,8 +369,11 @@
   (define (vals->cond key v)
     (cond ((eq? v 'else)   'else)
 	  ((null? v)       #f)
+	  ((symbol? v)     `(eq?  ,key ,(quote-value v)))
           ((atom? v)       `(eqv? ,key ,(quote-value v)))
 	  ((null? (cdr v)) `(eqv? ,key ,(quote-value (car v))))
+	  ((every symbol? v)
+	                   `(memq ,key ',v))
 	  (else            `(memv ,key ',v))))
   (let ((g (gensym)))
     `(let ((,g ,key))
--- a/femtolisp/test.lsp
+++ b/femtolisp/test.lsp
@@ -226,3 +226,35 @@
 	  (sub h n (string.inc h i) (cons i lst))
 	  (reverse! lst))))
   (sub haystack needle (if (null? offs) 0 (car offs)) ()))
+
+(let ((*profiles* (table)))
+  (set! profile
+	(lambda (s)
+	  (let ((f (top-level-value s)))
+	    (set-top-level-value! s
+	     (lambda args
+	       (define tt (get *profiles* s 0))
+	       (define t0 (time.now))
+	       (define v (apply f args))
+	       (put! *profiles* s (+ tt (- (time.now) t0)))
+	       v)))))
+  (set! show-profiles
+	(lambda ()
+	  (define (swapad c) (cons (cdr c) (car c)))
+	  (for-each (lambda (p)
+		      (princ (cdr p) "\t\t" (car p))
+		      (newline))
+		    (simple-sort (map swapad (table.pairs *profiles*)))))))
+
+#;(for-each profile
+	  '(emit encode-byte-code const-to-idx-vec
+	    index-of lookup-sym in-env?
+	    compile-sym compile-if compile-begin
+	    list-partition just-compile-args
+	    compile-arglist
+	    compile-app compile-let compile-call
+	    compile-in compile compile-f
+	    map length> length= count filter append
+	    lastcdr to-proper reverse reverse! list->vector
+	    table.foreach list-head list-tail assq memq assoc member
+	    assv memv nreconc bq-process))
--- a/femtolisp/torture.scm
+++ b/femtolisp/torture.scm
@@ -1,19 +1,24 @@
+(define ones (map (lambda (x) 1) (iota 1000000)))
+
+(display (apply + ones))
+(newline)
+
 (define (big n)
   (if (<= n 0)
       0
       `(+ 1 1 1 1 1 1 1 1 1 1 ,(big (- n 1)))))
 
-(define nst `(display ,(big 100000)))
+(define nst (big 100000))
 
 (display (eval nst))
 (newline)
 
+(define longg (cons '+ ones))
+(display (eval longg))
+(newline)
+
 (define (f x)
   (begin (display x)
 	 (newline)
 	 (f (+ x 1))
 	 0))
-
-(define longg (cons '+ (map (lambda (x) 1) (iota 1000000))))
-(display (eval longg))
-(newline)