Previous: , Up: machinery   [Index]


16.10 Local variables

We assume the validity of machinery simplifications to focus on some aspect of the runtime behaviour, Simplification assumptions.

Local variables are put on the Scheme stack in such a way that the stack frame can be left immutable if it is freezed in a Scheme continuation object. Immutable local variables and mutable local variables, before the first mutation, are just put in machine words on the stack frame; mutable local variables after the first mutation are put in the single slot of a Scheme vector whose reference is put on the stack.

Immutable local variables only

Let’s consider the following program in which the function one has only immutable local variables:

(import (rnrs))
(define (one A B)
  (let ((C 3)
        (D 4))
    (list A B C D)))
(one 1 2)

right after the call to one the arguments are on the stack below the return address, machinery locals.

      high memory
|                      |
|----------------------|         --
            .                    .
            .                    . caller
            .                    . stack frame
|----------------------|         .
|    return address    | <- FPR  .
|----------------------|         --
|    argument A == 1   |         .
|----------------------|         . stack frame
|    argument B == 2   |         . of ONE
|----------------------|         .
|                      |
       low memory

Figure 16.52: Stack frame with arguments.

Then the local values are put on the stack, right below the arguments, machinery locals.

      high memory
|                      |
|----------------------|                      --
            .                                 .
            .                                 . caller
            .                                 . stack frame
|----------------------|                      .
|    return address    | <- FPR               .
|----------------------|                      --
|    argument A == 1   | <- FPR - 1*wordsize  .
|----------------------|                      .
|    argument B == 2   | <- FPR - 2*wordsize  .
|----------------------|                      . stack frame
|       local C == 3   | <- FPR - 3*wordsize  . of ONE
|----------------------|                      .
|       local D == 4   | <- FPR - 4*wordsize  .
|----------------------|                      .
|                      |
       low memory

Figure 16.53: Stack frame with arguments and local variables.

We can imagine the function one compiled to pseudo–code as follows:

(define (one stack-slot-1 stack-slot-2)
  (let-on-stack ((stack-slot-3 3)
                 (stack-slot-4 4))
    (list stack-slot-1
          stack-slot-2
          stack-slot-3
          stack-slot-4)))

if the Assembly code needs to copy the value of the local variable stack-slot-3 into the CPU register AAR, it just does it as stack memory access:

(movl (disp (* -3 wordsize) FPR) AAR)

If a continuation object is created with this scenario on the stack: everything is ready, because the stack frame never needs to be mutated.

Mutable local variables

Let’s consider the following program in which the function one has both an immutable local variable and a mutable one:

(import (rnrs))
(define (one A B)
  (let ((C 3)
        (D 4))
    (display (list A B C D))
    (set! D 41)
    (display (list A B C D))
    (set! D 42)
    (display (list A B C D))))
(one 1 2)

after the call to one the arguments and the local variables are put on the stack below the return address, compiler machinery locals.

      high memory
|                      |
|----------------------|                      --
            .                                 .
            .                                 . caller
            .                                 . stack frame
|----------------------|                      .
|    return address    | <- FPR               .
|----------------------|                      --
|    argument A == 1   | <- FPR - 1*wordsize  .
|----------------------|                      .
|    argument B == 2   | <- FPR - 2*wordsize  .
|----------------------|                      . stack frame
|       local C == 3   | <- FPR - 3*wordsize  . of ONE
|----------------------|                      .
|       local D == 4   | <- FPR - 4*wordsize  .
|----------------------|                      .
|                      |
       low memory

Figure 16.54: Stack frame with arguments and local variables before the first local variable mutation.

Before the first local variable mutation (the set! form): if the Assembly code needs to copy the value of the local variable stack-slot-4 into the CPU register AAR, it just does it as stack memory access:

(movl (disp (* -3 wordsize) FPR) AAR)

because the value is just there. If a continuation object is created with this scenario on the stack: everything is ready, because the stack frame never needs to be mutated.

When the mutable local variable is assigned: something has to change. We can imagine the function one compiled to pseudo–code as follows:

(define (one stack-slot-1 stack-slot-2)
  (let-on-stack ((stack-slot-3 3)
                 (stack-slot-4 4))
    (display (list stack-slot-1
                   stack-slot-2
                   stack-slot-3
                   stack-slot-4))
    (set! stack-slot-4 (vector 41))
    (display (list stack-slot-1
                   stack-slot-2
                   stack-slot-3
                   ($vector-ref stack-slot-4 0)))
    ($vector-set! stack-slot-4 0 42)
    (display (list stack-slot-1
                   stack-slot-2
                   stack-slot-3
                   ($vector-ref stack-slot-4 0)))))

whenever the local variable assignment happens: a Scheme vector is allocated on the heap and filled with the new variable’s value; then a reference to such object is stored in the stack slot reserved to the local variable. From this point onwards: access to the local variable happens through the primitive operations $vector-ref and $vector-set!.

If a continuation object is created after the local variable first mutation: everything is ready, the stack frame does not need to be mutated because the mutable location is in the Scheme vector.

Mutable arguments

Mutable argument bindings are handled in the same way of mutable local variables. Let’s consider the following program in which the function one has a mutable argument:

(import (rnrs))
(define (one A)
  (display A)
  (set! A 2)
  (display A)
  (set! A 3)
  (display A))
(one 1)

We can imagine the function one compiled to pseudo–code as follows:

(define (one stack-slot-1)
  (display stack-slot-1)
  (set! stack-slot-1 (vector 2))
  (display ($vector-ref stack-slot-1 0))
  ($vector-set! stack-slot-1 0 3)
  (display ($vector-ref stack-slot-1 0))

Previous: , Up: machinery   [Index]