컨트롤러는 유지하고 연산과 저장 표현만 바꾼다.
한 컨트롤러가 특정 산술 방법 대신 이름 붙은 연산 계약에 의존하고, 한 제어 루프가 특정 저장 배치 대신 레지스터 bank 규약에 의존하도록 만듭니다.
기계 설계를 전체 재작성하지 않고 부분적으로 바꾸게 하는 경계는 무엇일까요?
- 컨트롤러 구조와 컨트롤러가 부르는 연산 패키지 분리하기
- 같은 컨트롤러를 유클리드식·뺄셈식 축소 패키지로 실행하기
- 컨트롤러를 바꾸지 않고 프로세스 비용 비교하기
- 레지스터 저장을 read와 write 메시지 뒤에 숨기기
- 하나의 추상화 경계를 측정된 두 구현에서 비교하기
축소 컨트롤러는 done?, next-a, next-b, answer만 압니다. 유클리드 패키지는 이 이름을 나머지 축소에 연결하고, 뺄셈 패키지는 반복 차이 축소에 연결합니다. 두 기계는 같은 조립 명령열로 같은 최대공약수를 반환하지만 명령 수는 서로 다른 프로세스를 드러냅니다.
두 번째 프로그램은 같은 카운터 컨트롤러에 레지스터 bank 두 개를 줍니다. 한 bank는 가변 연관 레코드를 쓰고 다른 bank는 이름-인덱스 표 뒤의 벡터를 씁니다. 컨트롤러는 read와 write 메시지만 보냅니다. 같은 유한 관찰은 이 클라이언트 계약이 저장 방식 변경을 견딘다는 사실을 보여 줍니다.
(begin
(define (tagged-list? value tag)
(and (pair? value) (eq? (car value) tag)))
(define (extract-labels controller position)
(cond ((null? controller) '())
((symbol? (car controller))
(cons (cons (car controller) position)
(extract-labels (cdr controller) position)))
(else
(extract-labels (cdr controller) (+ position 1)))))
(define (extract-instructions controller)
(cond ((null? controller) '())
((symbol? (car controller))
(extract-instructions (cdr controller)))
(else
(cons (car controller)
(extract-instructions (cdr controller))))))
(define (make-registers names)
(map (lambda (name) (cons name '*unassigned*)) names))
(define (make-machine register-names operations controller)
(list '*machine*
(make-registers register-names)
(cons '*stack* '())
operations
(extract-instructions controller)
(extract-labels controller 0)
(cons 'pc 0)
(cons 'flag #f)
(cons 'halted #f)
(cons 'steps 0)))
(define (machine-registers machine) (list-ref machine 1))
(define (machine-stack machine) (list-ref machine 2))
(define (machine-operations machine) (list-ref machine 3))
(define (machine-instructions machine) (list-ref machine 4))
(define (machine-labels machine) (list-ref machine 5))
(define (machine-pc-cell machine) (list-ref machine 6))
(define (machine-flag-cell machine) (list-ref machine 7))
(define (machine-halted-cell machine) (list-ref machine 8))
(define (machine-steps-cell machine) (list-ref machine 9))
(define (machine-pc machine) (cdr (machine-pc-cell machine)))
(define (set-machine-pc! machine value)
(set-cdr! (machine-pc-cell machine) value))
(define (machine-flag machine) (cdr (machine-flag-cell machine)))
(define (set-machine-flag! machine value)
(set-cdr! (machine-flag-cell machine) value))
(define (machine-halted? machine) (cdr (machine-halted-cell machine)))
(define (halt-machine! machine)
(set-cdr! (machine-halted-cell machine) #t))
(define (machine-steps machine) (cdr (machine-steps-cell machine)))
(define (increment-machine-steps! machine)
(set-cdr! (machine-steps-cell machine)
(+ (machine-steps machine) 1)))
(define (register-cell machine name)
(let ((cell (assoc name (machine-registers machine))))
(if cell cell (error "unknown register" name))))
(define (get-register machine name)
(cdr (register-cell machine name)))
(define (set-register! machine name value)
(set-cdr! (register-cell machine name) value))
(define (push! machine value)
(set-cdr! (machine-stack machine)
(cons value (cdr (machine-stack machine)))))
(define (pop! machine)
(let ((values (cdr (machine-stack machine))))
(if (null? values)
(error "empty machine stack")
(let ((value (car values)))
(set-cdr! (machine-stack machine) (cdr values))
value))))
(define (stack-empty? machine)
(null? (cdr (machine-stack machine))))
(define (operation machine name)
(let ((binding (assoc name (machine-operations machine))))
(if binding (cdr binding) (error "unknown operation" name))))
(define (label-position machine name)
(let ((binding (assoc name (machine-labels machine))))
(if binding (cdr binding) (error "unknown label" name))))
(define (evaluate-source machine source)
(cond ((tagged-list? source 'const) (cadr source))
((tagged-list? source 'reg)
(get-register machine (cadr source)))
((tagged-list? source 'label)
(label-position machine (cadr source)))
((tagged-list? source 'op)
(apply (operation machine (cadr source))
(map (lambda (operand)
(evaluate-source machine operand))
(cddr source))))
(else
(error "unknown machine source" source))))
(define (advance! machine)
(set-machine-pc! machine (+ (machine-pc machine) 1)))
(define (execute-one! machine)
(let* ((instruction
(list-ref (machine-instructions machine)
(machine-pc machine)))
(tag (car instruction)))
(increment-machine-steps! machine)
(cond
((eq? tag 'assign)
(set-register! machine
(cadr instruction)
(evaluate-source machine (caddr instruction)))
(advance! machine))
((eq? tag 'test)
(set-machine-flag! machine
(evaluate-source machine (cadr instruction)))
(advance! machine))
((eq? tag 'branch)
(if (machine-flag machine)
(set-machine-pc!
machine
(label-position machine (cadr instruction)))
(advance! machine)))
((eq? tag 'goto)
(set-machine-pc! machine
(evaluate-source machine (cadr instruction))))
((eq? tag 'save)
(push! machine (get-register machine (cadr instruction)))
(advance! machine))
((eq? tag 'restore)
(set-register! machine (cadr instruction) (pop! machine))
(advance! machine))
((eq? tag 'perform)
(evaluate-source machine (cadr instruction))
(advance! machine))
((eq? tag 'halt)
(halt-machine! machine))
(else
(error "unknown machine instruction" instruction)))))
(define (run-machine! machine step-limit)
(cond ((machine-halted? machine) 'complete)
((= step-limit 0) 'step-limit)
(else
(execute-one! machine)
(run-machine! machine (- step-limit 1)))))
(define controller
'(loop
(test (op done? (reg a) (reg b)))
(branch done)
(assign next-a (op next-a (reg a) (reg b)))
(assign next-b (op next-b (reg a) (reg b)))
(assign a (reg next-a))
(assign b (reg next-b))
(goto (label loop))
done
(assign answer (op answer (reg a) (reg b)))
(halt)))
(define euclid-operations
(list
(cons 'done? (lambda (a b) (= b 0)))
(cons 'next-a (lambda (a b) b))
(cons 'next-b (lambda (a b) (remainder a b)))
(cons 'answer (lambda (a b) a))))
(define subtraction-operations
(list
(cons 'done? (lambda (a b) (= a b)))
(cons 'next-a
(lambda (a b) (if (> a b) (- a b) a)))
(cons 'next-b
(lambda (a b) (if (> b a) (- b a) b)))
(cons 'answer (lambda (a b) a))))
(define (run-with operations)
(let ((machine
(make-machine
'(a b next-a next-b answer)
operations
controller)))
(set-register! machine 'a 206)
(set-register! machine 'b 40)
(list (run-machine! machine 200)
(get-register machine 'answer)
(machine-steps machine)
(machine-instructions machine))))
(let ((euclid (run-with euclid-operations))
(subtraction (run-with subtraction-operations)))
(list (list 'euclid (car euclid) (cadr euclid) (caddr euclid))
(list 'subtraction
(car subtraction)
(cadr subtraction)
(caddr subtraction))
(equal? (list-ref euclid 3)
(list-ref subtraction 3))))
)- 출력
- —
- 값
- —
- 진단
- —
연산 패키지 프로그램은 ((euclid complete 2 32) (subtraction complete 2 95) #t)를 반환합니다. 레지스터 bank 프로그램은 ((4 10) (4 10) #t)를 반환합니다.
첫 실행에서는 두 기계가 같은 명령 태그를 가져오지만 연산 조회가 다른 프로시저를 고르는지 확인하세요. 유클리드 축소 네 번과 뺄셈 축소 열세 번을 비교하세요. 두 번째 실행에서는 같은 read/write 호출 수열이 서로 다른 숨은 저장 연산으로 들어가는 과정을 따라가세요.
프로그램을 수정하고 결과를 비교해 보세요.
이진 GCD 연산 패키지나 세 번째 레지스터 bank 표현을 추가하세요. 컨트롤러는 그대로 두고 새 구현이 지켜야 할 연산 또는 저장 계약을 적으세요.
힌트 보기
연산 패키지는 끝나지 않은 각 전이를 자기 done? 조건 쪽으로 진행시켜야 합니다. bank는 컨트롤러가 사용하는 write 뒤 read 관찰을 보존해야 합니다.