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
fis compiled, it creates a compiled procedure and stores it inenv. - Since
fcontains a call tog, the compiled procedure will also contain instructions to invokegwhich required first to lookupgand store the result of lookup inproc. - Then compiled procedure will contain instructions to evaluate all the arguments of
gand place those arguments inargl. - Now, let’s talk about evaluator. When
gis evaluated/interpreted, it also gets stored in the sameenv. Thus the instructions generated by compiler to lookupgwill now return the procedureg! - How evaluator invokes a compund procedure? It starts from entry point
compound-apply, and at that point, regprocshould 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-applystored incompapp. - For (ii), We use
continuefor return. We know that evaluator expects that while incompound-applythe 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-applsimilar 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.