Intermediate Language

BiwaScheme is an IL based Compiler-Interpreter.

Most of the opecodes are inherited from 3imp_pdf.

Registers:

Opecodes

constant

is used to push a value on to the stack.

op_constant is often used with the [[op_argument]].

example

program:

(display "hello")

compiled:

[frame
   [constant "hello"
   [argument
   [constant 1
   [argument
   [refer-global "display"
   [apply]]]]]]
[halt]]

argument

is used to push a value (typically an argument of function call) onto the stack.

format

["argument", <opecodes_next>]

registers

example

program:

(print "hi")

compiled:

[frame
   [constant "hi"
   [argument
   [constant 1
   [argument
   [refer-global "print"
   [apply]]]]]]
[halt]]

test

is used to stop the evaluation of the program.

format

["test", , ]

example

program:

'()

compiled:

[constant 7
[test
   [constant 8
      [halt]]
   [constant 9
      [halt]]]]

close

Creates closure object.

format

["close", , , , ]

TODO: <opecodes_next> should be the last in consistency with other opecodes

description

op_close creates closure object onto the 'a' register.

h2. closure object

Scheme closures are represented by JS arrays with its attribute 'closure_p' assigned to true.

[body, freevar1, freevar2, ..., dotpos]

example

program:

(lambda () 1)

compiled:

[close 
  0
  [constant 1
    [return]]
  [halt]
  -1]

h2. example of reassigned freevars

Usually closure object contains actual values of the freevars:

(define (f a b)
  (lambda () a b))
(f 11 22)

-> [ ["refer-free", 0, ["refer-free", 1, ["return"]]], 11, 22, -1]

When the freevar may reassigned by set!, a box (JS array contains the actual value) is stored. It will be unboxed by [[op_indirect]] (see also: [[op_indirect]], [[op_box]])

(define (f a b)
  (set! a 99)
  (lambda () a b))
(f 11 22)

-> [ ["refer-free", 0, ["indirect", ["refer-free", 1, ["return"]]]], [99], 22, -1]

conti

is used to capture the continuation.

The captured continuation is invoked by nuate.

format

["conti", , ]

example

program:

(call/cc func)

compiled:

[frame
   [conti 0
   [argument
   [constant 1
   [argument
   [refer-global "func"
   [apply]]]]]]
[halt]]

nuate

is used to invoke a continuation.

format

["nuate", , ]

description

op_nuate takes saved stack as an argument. When op_nuate is invoked, the interpreter stack is replaced by this saved stack.

op_nuate is never generated by Compiler. It is dynamically generated by Interpreter when processing op_conti.

example

program:

(define cc (call/cc identity))
(cc)

compiled:

[refer-local 0
[nuate #
[return]]]

halt

halt is used to stop the evaluation of the program.

example

program:

'()

compiled:

[constant ()
  [halt]]

frame

Pushes stack frame.

op_frame must be called before applying a function, except when it is a tail call (see also: shift.)

format

["frame", , ]

description

op_frame pushes a frame (i.e. the following values) onto stack:

Usually this frame is popped by return.

op_frame is generated by:

example

program:

(print 11)

compiled:

[frame        // op_frame
   [constant 11          // push arguments to stack           
   [argument
   [constant 1           // push number of arguments 
   [argument
   [refer-global "print" // load function to 'a' register
   [apply]]]]]]          // invoke function
[halt]]

Note that op_frame itself does not touch 'f' register. Since function arguments are evaluated between op_frame and apply and these evaluations must be done in the current frame, 'f' register is updated by apply.

apply

Invokes Scheme closure, library function or JS function.

format

["apply"]

description

In contrast to other opecodes, op_apply takes no arguments. Instead it retrieves needed information from the stack.

Before calling op_apply, you must put these values onto stack:

h2. applicable things

Note that you cannot invoke pure JS functions directly. You can use js-call or js-invoke instead.

example

program:

(+ 11 22)

compiled:

[frame
   [constant 22  
   [argument          // push 22 (second argument) to stack
   [constant 11  
   [argument          // push 11 (first argument) to stack
   [constant 2   
   [argument          // push 2 (number of arguments) to stack
   [refer-global "+"  // load function '+' to 'a' register
   [apply]]]]]]]]     // invoke the function with arguments 11 and 22
[halt]]

return

Pops stack frame.

op_return is generated by (lambda ...) and must be called as the last opecode of function execution.

format

["return"]

op_return does not have because it pops next opecodes from stack.

description

op_return pops the following values from stack:

op_return also sets x, f, c to register 'x', 'f', 'c'.

example

program:

((lambda () 11))

compiled:

[frame
   [constant 0
   [argument
   [close 0
      [constant 11
      [return]]
      [apply]
   -1]]]
[halt]]

shift

Discards the arguments for parent call from stack.

Since stack frame is not pushed by frame and popped by return for tail calls, we need to remove old arguments with op_shift.

format

["shift", , ]

description

op_shift removes arguments for parent call from stack. Suppose the following code:

(define (foo x y)
  (bar "a" "b" "c"))
(foo "x" "y")

Before applying bar, op_shift ["shift", 3, ["apply"]] removes the following values. Note that first four elements will not be popped - that's why this operation is named "shift".

Also note that stack frame is not pushed by [[op_frame]] for tail calls.

example

program:

((lambda () (print 1))

Here (print 1) is a tail call.

compiled:

[frame
   [constant 0
   [argument
   [close 0  // make a closure, and
      [constant 1
      [argument
      [constant 1
      [argument
      [refer-global "print"
      [shift 1
      [apply]]]]]]]
      [apply]  // immediately invoke it
   -1]]]
[halt]]

Without tail call optimization, this program is compiled to:

[frame
   [constant 0
   [argument
   [close 0
      [frame  // push stack frame
         [constant 1
         [argument
         [constant 1
         [argument
         [refer-global "print"
         [apply]]]]]]
      [return]]  // pop stack frame
      [apply]
   -1]]]
[halt]]

refer-local

is used to load the value of a local variable.

format

["refer-local", , ]

example

program:

(define a 99)

compiled:

[frame
   [constant 7
   [argument
   [constant 1
   [argument
   [close 0
      [refer-local 0
         [return]]
      [apply]
      -1]]]]]
[halt]]

refer-free

is used to load the value of a free variable.

format

["refer-free", , ]

example

program:

(let ((a "a")(b "b"))
  (let ()
    a)

compiled:

[frame
   [constant "a"
   [argument
   [constant "b"
   [argument
   [constant 2
   [argument
   [close 0
      [constant 0
      [argument
      [refer-local 1
      [argument
      [close 1
         [refer-free 0
         [return]]
         [shift 0
         [apply]]
      -1]]]]]
      [apply]
   -1]]]]]]]
halt]

refer-global

is used to load the value of a certain global variable.

example

program:

map

compiled:

[refer-global "map"
[halt]]

assign-global

is used to assign a value to a global variable.

format

["assign-global", , ]

example

program:

(define a 99)

compiled:

[constant 99
[assign-global "a"
[halt]]]

assign-local

is used to assign value to a variable.

format

["assign-local", , ]

example

See example of [[op_indirect]].

assign-free

is used to assign a value to a free variable.

format

["assign-free", , ]

example

program:

(let ((a "a"))
  (let ()
    (set! a "aa")))

compiled:

[frame
   [constant "a"
   [argument
   [constant 1
   [argument
   [close 0
      [box 0
      [constant 0
      [argument
      [refer-local 0
      [argument
      [close 1
         [constant "aa"
         [assign-free 0
         [return]]]
         [shift 0
         [apply]]
      -1]]]]]]
      [apply]
   -1]]]]]
halt]

box

is used to make a box for a variable which may be reassigned by set!.

format

["box", , ]

example

See example of [[op_indirect]].

indirect

is used to load the value of a variable which may reassigned by 'set!'.

format

["indirect", ]

example

program:

(let ((a "a"))
  (set! a "b")
  a)

compiled:

[frame
   [constant "a"
   [argument
   [constant 1
   [argument
   [close 0
      [box 0
      [constant "b"
      [assign-local 0
      [refer-local 0
      [indirect
      [return]]]]]]
      [apply]
   -1]]]]]
[halt]]