Chapter 5, Computing with Register Machines
Exercise 5.47
Interesting!
Few points how it will work by using the example suggested for testing(compiled f
calls interpreted g
):
- When
f
is compiled, it creates a compiled procedure and stores it inenv
. - Since
f
contains a call tog
, the compiled procedure will also contain instructions to invokeg
which required first to lookupg
and store the result of lookup inproc
. - Then compiled procedure will contain instructions to evaluate all the arguments of
g
and place those arguments inargl
. - Now, let’s talk about evaluator. When
g
is evaluated/interpreted, it also gets stored in the sameenv
. Thus the instructions generated by compiler to lookupg
will now return the procedureg
! - How evaluator invokes a compund procedure? It starts from entry point
compound-apply
, and at that point, regproc
should contain the required procedure to be evaluated and arguments should be inargl
. - Well, this has already been done by the instructions generated by compiler for invoking
g
. - So, what’s the missing piece? Just two things: (i) how to transfer the control to evaluator from compiler and then (ii) how to return back to the compiler intructions after procedure gets executed by the evaluator instructions.
- For (i), while calling the procedure in the compiler, we just have to do one more test apart from the existing test of
primitive-procedure?
, now we also check forcompound-procedure?
and jump to the code for calling compound procedure. As suggested in the exercise we jump to the locationcompound-apply
stored incompapp
. - For (ii), We use
continue
for return. We know that evaluator expects that while incompound-apply
the place to return is stored on top of the stack. So we just need to save the place where we have to return on top of the stack! This can be done by writing a parallel a new procedure, saycompound-proc-appl
similar tocompiled-proc-appl
. And for each of the cases we save the register where we have to return on top of the stack!
That’s it!
Here are the changes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
(define (compile-procedure-call target linkage)
(let ((primitive-branch (make-label 'primitive-branch))
(compiled-branch (make-label 'compiled-branch))
(compound-branch (make-label 'compound-branch)) ;;;
(after-call (make-label 'after-call)))
(let ((compiled-or-compound-linkage
(if (eq? linkage 'next) after-call linkage)))
(append-instruction-sequences
(make-instruction-sequence '(proc) '()
`((test (op primitive-procedure?) (reg proc))
(branch (label ,primitive-branch))))
(parallel-instruction-sequences
(append-instruction-sequences ;;;
(make-instruction-sequence '(proc) '() ;;;
`((test (op compound-procedure?) (reg proc)) ;;;
(branch (label ,compound-branch)))) ;;;
(parallel-instruction-sequences ;;;
(append-instruction-sequences ;;;
compiled-branch ;;;
(compile-proc-appl target compiled-or-compound-linkage)) ;;;
(append-instruction-sequences ;;;
compound-branch ;;;
(compound-proc-appl target compiled-or-compound-linkage)))) ;;;
(append-instruction-sequences
primitive-branch
(end-with-linkage linkage
(make-instruction-sequence '(proc argl)
(list target)
`((assign ,target
(op apply-primitive-procedure)
(reg proc)
(reg argl)))))))
after-call))))
;;;new procedure
;;;applying compound procedures
(define (compound-proc-appl target linkage)
(cond ((and (eq? target 'val) (not (eq? linkage 'return)))
(make-instruction-sequence '(proc) all-regs
`((assign continue (label ,linkage))
(save continue)
(goto (reg compapp)))))
((and (not (eq? target 'val))
(not (eq? linkage 'return)))
(let ((proc-return (make-label 'proc-return)))
(make-instruction-sequence '(proc) all-regs
`((assign continue (label ,proc-return))
(save continue)
(goto (reg compapp))
,proc-return
(assign ,target (reg val))
(goto (label ,linkage))))))
((and (eq? target 'val) (eq? linkage 'return))
(make-instruction-sequence '(proc continue) all-regs
'((save continue)
(goto (reg compapp)))))
((and (not (eq? target 'val)) (eq? linkage 'return))
(error "return linkage, target not val -- COMPILE"
target))))
Example/Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1 ]=> (compile-and-go '(define (f x) (g x)))
(total-pushes = 0 maximum-depth = 0)
;;; EC-Eval value:
ok
;;; EC-Eval input:
(define (g x) x)
(total-pushes = 3 maximum-depth = 3)
;;; EC-Eval value:
ok
;;; EC-Eval input:
(f 'hello)
(total-pushes = 6 maximum-depth = 3)
;;; EC-Eval value:
hello
;;; EC-Eval input:
(define (g x) 'ignored-x)
(total-pushes = 3 maximum-depth = 3)
;;; EC-Eval value:
ok
;;; EC-Eval input:
(f 'hello)
(total-pushes = 6 maximum-depth = 3)
;;; EC-Eval value:
ignored-x
;;; EC-Eval input:
(define (g x) (+ x 100))
(total-pushes = 3 maximum-depth = 3)
;;; EC-Eval value:
ok
;;; EC-Eval input:
(f 2)
(total-pushes = 14 maximum-depth = 5)
;;; EC-Eval value:
102
;;; EC-Eval input:
Comments
It’s amazing to see this inter-operatibility. The evaluator was already designed/setup with this in mind that things won’t get in conflict with compiler and the two can even co-exist together and can even invoke procedures from each other.