guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

What happened to the ex-Guile VM?


From: Keisuke Nishida
Subject: What happened to the ex-Guile VM?
Date: Mon, 19 Mar 2001 19:08:08 -0500
User-agent: Wanderlust/2.4.0 (Rio) SEMI/1.13.7 (Awazu) FLIM/1.13.2 (Kasanui) Emacs/21.0.99 (i686-pc-linux-gnu) MULE/5.0 (SAKAKI)

Hello,

I found somebody was working on a bytecode interpreter (VM) for
Guile around 1997.  What happened to it?  Why does it not exist
now?  The message I read was about the VM's unsatisfactory
efficiency.  Was it the only reason that Guile did not adopt a
VM?  Or is there any fatal reason of not supporting a VM?

Anyway, this is a brief summary of the current status of my VM.

In my VM implementation, a VM is a first-class object.  You can
create any number of VMs and run them separately, which I guess
helps supporting multithread programming in future.

The current VM has two different "engines".  One is for fast
execution, and one is for debugging execution.  The slowdown in
the debug engine seems to be about 30%, so probably most people
will be satisfied with the debug engine.  I use it by default.

Let's see the execution speed in a simple recursion:

  % guile  ;; debug evaluator
  guile> (define (fib n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))
  guile> (time (fib 30))
  clock utime stime cutime cstime gctime
  23.81 23.53  0.14   0.00   0.00  12.76
  $2 = 1346269

  % guile-vm  ;; debug engine
  address@hidden> ,time (fib 30)
  $3 = 1346269
  clock utime stime cutime cstime gctime
   3.12  3.11  0.00   0.00   0.00   0.00

  % rep  ;; version 0.13.4
  user> ,time (fib 30)
  1346269
  Elapsed: 4.372812 seconds

  % bigloo -O6 fib.scm ;; version 2.2b
  % time a.out
  1346269
  real  0m0.271s
  user  0m0.240s
  sys   0m0.020s

Evaluation of an expression is done in the following 6 steps:

1. Read

  "(let ((x 1) (y 2)) (+ x y))"  ->  (let ((x 1) (y 2)) (+ x y))

2. Expansion

  ->  ((lambda (x y) (+ x y)) 1 2)

3. Translation

  ->  ((@lambda (x y) (@+ x y)) 1 2)

4. Compilation

  ->  (@asm ()
        (const 1)
        (const 2)
        (@asm (x y)
          (ref x)
          (ref y)
          (add)
          (return))
        (tail-call 2))

5. Assemblage

   #<program 0>
   0  make-int8 1    ;; 1
   2  make-int8 2    ;; 2
   4  object-ref:0   ;; #<program 1>
   5  tail-call:2

   #<program 1>
   0  local-ref:1    ;; (ref x)
   1  local-ref:0    ;; (ref y)
   2  add
   3  return

6. Execution

  => 3

(In practice, these steps are done more efficiently.)

Compiled code is converted into a loadable bytecode and
loaded/linked by the VM.  For example, the following code

  foo

is compiled into the assembly

  (@asm ()
    (ref foo)
    (return))

which is converted into the loadable code

  load-symbol 3 "foo"
  link/module
  vector 1
  make-program 3 "%#&"

which can be saved in a file.  Compiled files have become much
smaller than before.  The dump routine is written in Scheme
(80 lines) and the undump routine has become part of the VM
(50 lines).  I hope the loading speed will become quite fast.

As shown above, symbols can be linked with modules at loading
time (or otherwise, they are delayed until run time), so there
is no run-time lookup of variables.  The VM-level module support
is as follows.

There are four kinds of variables:

 local variable    - allocated in the stack
 external variable - allocated in the heap
 module variable   - allocated in a module
 global variable   - looked up in the module hierarchy

If you enter a symbol at top level, it is inserted into the
current module (yes, I'll support it).  Modules are usually
organized into a hierarchy and have their global names.
Hence, variables can also be reached by their global names.
The VM provides an instruction `global-ref', which does
global lookup and finds a value cell.  This is all the VM
does for a module system.

More advanced stuff like importing/exporting variables is
supported by the intermediate language and the compiler.
All variables are statically identified at compile time,
and the compiler produces `global-ref' as necessary.  The
compiler must also take care of run-time recompilation of
pre-compiled code during an interactive session.

All languages, including Scheme, are to be translated into an
intermediate language, called GCIL (formerly called iScheme).
The following code is part of the definition of the R5RS core
environment, written in GCIL:

  (@define-module Language::R5RS::core)

  (@define car (@lambda (x) (@car x)))
  (@define cdr (@lambda (x) (@cdr x)))
  ...

The following is the definition of the language R5RS:

  (define-language r5rs
    :title        "Standard Scheme (R5RS + syntax-case)"
    :version      "0.3"
    :reader       read
    :expander     expand
    :translator   translate
    :printer      write
    :environment  (global-ref 'Language::R5RS::core)
    )

The following procedures are provided:

  (read-in PORT LANG)
  (compile-in FORM ENV LANG . OPTS)
  (compile-file-in SOURCE TARGET LANG . OPTS)
  (eval-in FORM ENV LANG)
  (load-in FILE LANG)
  (print-in VAL PORT LANG)

And the new repl manages the current language and modules...

I think I need more time to complete this project.  I haven't
completed my new module system yet.  I haven't implemented
exception handling yet.  The compiler is far from self-compiling
yet.  I want to at least make part of the compiler self-compilable
before the next snapshot.

I'm getting busy these days and stopping programming for a while.
Hopefully, I will be able to concentrate on this project next year.
I'm going to major in computer languages at a graduate school.

Sorry about my explanation being unclear.  I just wanted to let you
know I am still working on Guile VM and going to continue doing ;)

Kei



reply via email to

[Prev in Thread] Current Thread [Next in Thread]