SICP 9a
Register Machines
Table of Contents
Copyright notice: the two diagrams on this page are sourced from Unofficial Texinfo Format, licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
1 Agenda
So far, we have covered how to organise big programs. Now, we will see how those mechanisms work; we shall take simple LISP programs, and build the corresponding machines. The next lecture will show how an evaluator can be turned into hardware.
2 Euclid's algorithm
Let's see how the following iterative algorithm can be expressed as a machine.
(define (gcd a b) (if (= b 0) a (gcd b (remainder a b))))
Every single-processor computer comprises of two major parts: data paths and a controller.
2.1 Data paths
Registers store numbers.
2.2 Controller
Every machine has a state. The machine state is the state of the controller.
2.3 In writing
Diagrams take up a lot of space (and can take a long time to draw on a computer!), so we will describe the machine in a language.
(define-machine gcd (registers a b t) (controller loop (branch (zero? (fetch b)) done) (assign t (remainder (fetch a) (fetch b))) (assign a (fetch b)) (assign b (fetch t)) (goto loop) done))
2.4 Reading
Computers also require input from the outside occasionally. read
implements
this functionality.
(define-machine gcd (registers a b t) (controller main (assign a (read)) (assign b (read)) loop (branch (zero? (fetch b)) done) (assign t (remainder (fetch a) (fetch b))) (assign a (fetch b)) (assign b (fetch t)) (goto loop) done (perform (print (fetch a))) (goto main)))
2.5 Remainder
We have assumed remainder
is a basic operation, but it is actually a
whole machine in of itself.
(define (remainder n d) (if (< n d) n (remainder (- n d) d)))
In a practical computer, the proper way to implement remainder
would be using
binary notation and shift.
3 Factorial
So far we have seen how an iterative process can be turned into a machine. How about a recursive process?
(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))
With recursive processes, we have the problem that the machine contains itself inside of it, leading to a potentially infinitely large machine. We have to create the illusion of infinity somewhere in the machine.
Organisation of Register Machine
stack <—> data paths <—> finite-state controller
- Controller: finite and very simple.
- Data paths: registers and operators.
- Stack: not infinite, just very large (memory).
To create the illusion of infinity, we store the information in the stack required after the inner machine runs to resume the operation of the outer machine. Since they're nested in a recursive manner, the stack will only be accessed in a last-in first-out (LIFO) manner. So only a small part of the stack memory needs to be accessible.
Controller
(assign continue done) loop (branch (= (fetch n)) base) (save continue) (save n) (assign n (-1+ (fetch n))) (assign continue aft) (goto loop) aft (restore n) (restore continue) (assign val (+ (fetch n) (fetch val))) (goto (fetch continue)) base (assign val (fetch n)) (goto (fetch continue)) done
4 Fibonacci
Let's see how the stack holds up with a doubly recursive algorithm.
(define (fib n) (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))
Controller
(assign continue f-bdone) fib-loop ; n contains arg, continue is recipient (branch (<(fetch n) 2) immediate-ans) (save continue) (assign continue after-fib-n-1) (save n) (assign n (- (fetch n) 1)) (goto fib-loop) after-fib-n-1 (restore n) (restore continue) ; not needed, since no call before save (assign n (- (fetch n) 2)) (save continue) ; since continue not used, can also be removed (assign continue after-fib-n-2) (save val) (goto fib-loop) after-fib-n-2 (assign n (fetch val)) ; fib(n-2) (restore val) (assign val (+ (fetch val) (fetch n))) (goto (fetch continue)) immediate-ans (assign val (fetch n)) (goto (fetch continue)) fib-done
When using the stack, use discipline. Only save things which are needed later.
Note: there's a fantastic cough at 57:17.