This section describes the details on how instruction sequences are represented and combined. Recall from section 5.5.1 that an instruction sequence is represented as a list of the registers needed, the registers modified, and the actual instructions. We will also consider a label (symbol)(string) to be a degenerate case of an instruction sequence, which doesn't need or modify any registers. So to determine the registers needed and modified by instruction sequences we use the selectors
Original | JavaScript |
(define (registers-needed s) (if (symbol? s) '() (car s))) (define (registers-modified s) (if (symbol? s) '() (cadr s))) (define (statements s) (if (symbol? s) (list s) (caddr s))) | function registers_needed(s) { return is_string(s) ? null : head(s); } function registers_modified(s) { return is_string(s) ? null : head(tail(s)); } function instructions(s) { return is_string(s) ? list(s) : head(tail(tail(s))); } |
Original | JavaScript |
(define (needs-register? seq reg) (memq reg (registers-needed seq))) (define (modifies-register? seq reg) (memq reg (registers-modified seq))) | function needs_register(seq, reg) { return ! is_null(member(reg, registers_needed(seq))); } function modifies_register(seq, reg) { return ! is_null(member(reg, registers_modified(seq))); } |
The basic combiner is append-instruction-sequences. append_instruction_sequences. This takes as arguments an arbitrary number of two instruction sequences that are to be executed sequentially and returns an instruction sequence whose statements are the statements of all the the two sequences appended together. The subtle point is to determine the registers that are needed and modified by the resulting sequence. It modifies those registers that are modified by any of the sequences; are modified by either sequence; it needs those registers that must be initialized before the first sequence can be run (the registers needed by the first sequence), together with those registers needed by any of the other sequences that are not initialized (modified) by sequences preceding it. the second sequence that are not initialized (modified) by the first sequence.
The sequences are appended two at a time by append-2-sequences. This The function append_instruction_sequences is given two instruction sequences seq1 and seq2 and returns the instruction sequence whose statements instructions are the statements instructions of seq1 followed by the statements instructions of seq2, whose modified registers are those registers that are modified by either seq1 or seq2, and whose needed registers are the registers needed by seq1 together with those registers needed by seq2 that are not modified by seq1. (In terms of set operations, the new set of needed registers is the union of the set of registers needed by seq1 with the set difference of the registers needed by seq2 and the registers modified by seq1.) Thus, append-instruction-sequences append_instruction_sequences is implemented as follows:
Original | JavaScript |
(define (append-instruction-sequences . seqs) (define (append-2-sequences seq1 seq2) (make-instruction-sequence (list-union (registers-needed seq1) (list-difference (registers-needed seq2) (registers-modified seq1))) (list-union (registers-modified seq1) (registers-modified seq2)) (append (statements seq1) (statements seq2)))) (define (append-seq-list seqs) (if (null? seqs) (empty-instruction-sequence) (append-2-sequences (car seqs) (append-seq-list (cdr seqs))))) (append-seq-list seqs)) | function append_instruction_sequences(seq1, seq2) { return make_instruction_sequence( list_union(registers_needed(seq1), list_difference(registers_needed(seq2), registers_modified(seq1))), list_union(registers_modified(seq1), registers_modified(seq2)), append(instructions(seq1), instructions(seq2))); } |
This procedure function uses some simple operations for manipulating sets represented as lists, similar to the (unordered) set representation described in section 2.3.3:
Original | JavaScript |
(define (list-union s1 s2) (cond ((null? s1) s2) ((memq (car s1) s2) (list-union (cdr s1) s2)) (else (cons (car s1) (list-union (cdr s1) s2))))) (define (list-difference s1 s2) (cond ((null? s1) '()) ((memq (car s1) s2) (list-difference (cdr s1) s2)) (else (cons (car s1) (list-difference (cdr s1) s2))))) | function list_union(s1, s2) { return is_null(s1) ? s2 : is_null(member(head(s1), s2)) ? pair(head(s1), list_union(tail(s1), s2)) : list_union(tail(s1), s2); } function list_difference(s1, s2) { return is_null(s1) ? null : is_null(member(head(s1), s2)) ? pair(head(s1), list_difference(tail(s1), s2)) : list_difference(tail(s1), s2); } |
Preserving, The function preserving, the second major instruction sequence combiner, takes a list of registers regs and two instruction sequences seq1 and seq2 that are to be executed sequentially. It returns an instruction sequence whose statements instructions are the statements instructions of seq1 followed by the statements instructions of seq2, with appropriate save and restore instructions around seq1 to protect the registers in regs that are modified by seq1 but needed by seq2. To accomplish this, preserving first creates a sequence that has the required saves followed by the statements instructions of seq1 followed by the required restores. This sequence needs the registers being saved and restored in addition to the registers needed by seq1, and modifies the registers modified by seq1 except for the ones being saved and restored. This augmented sequence and seq2 are then appended in the usual way. The following procedure function implements this strategy recursively, walking down the list of registers to be preserved:[1]
Original | JavaScript |
(define (preserving regs seq1 seq2) (if (null? regs) (append-instruction-sequences seq1 seq2) (let ((first-reg (car regs))) (if (and (needs-register? seq2 first-reg) (modifies-register? seq1 first-reg)) (preserving (cdr regs) (make-instruction-sequence (list-union (list first-reg) (registers-needed seq1)) (list-difference (registers-modified seq1) (list first-reg)) (append `((save ,first-reg)) (statements seq1) `((restore ,first-reg)))) seq2) (preserving (cdr regs) seq1 seq2))))) | function preserving(regs, seq1, seq2) { if (is_null(regs)) { return append_instruction_sequences(seq1, seq2); } else { const first_reg = head(regs); return needs_register(seq2, first_reg) && modifies_register(seq1, first_reg) ? preserving(tail(regs), make_instruction_sequence( list_union(list(first_reg), registers_needed(seq1)), list_difference(registers_modified(seq1), list(first_reg)), append(list(save(first_reg)), append(instructions(seq1), list(restore(first_reg))))), seq2) : preserving(tail(regs), seq1, seq2); } } |
Another sequence combiner,
tack-on-instruction-sequence,
tack_on_instruction_sequence,
is used by
compile-lambda
compile_lambda_expression
to append a
procedure
function
body to another sequence. Because the
procedure
function
body is not in line
to be executed as part of the combined
sequence, its register use has no impact on the register use of the sequence
in which it is embedded. We thus ignore the
procedure
function
body's sets of needed and modified
registers when we tack it onto the other sequence.
Original | JavaScript |
(define (tack-on-instruction-sequence seq body-seq) (make-instruction-sequence (registers-needed seq) (registers-modified seq) (append (statements seq) (statements body-seq)))) | function tack_on_instruction_sequence(seq, body_seq) { return make_instruction_sequence( registers_needed(seq), registers_modified(seq), append(instructions(seq), instructions(body_seq))); } |
Compile-if The functions compile_conditional and compile-procedure-call compile_function_call use a special combiner called parallel-instruction-sequences parallel_instruction_sequences to append the two alternative branches that follow a test. The two branches will never be executed sequentially; for any particular evaluation of the test, one branch or the other will be entered. Because of this, the registers needed by the second branch are still needed by the combined sequence, even if these are modified by the first branch.
Original | JavaScript |
(define (parallel-instruction-sequences seq1 seq2) (make-instruction-sequence (list-union (registers-needed seq1) (registers-needed seq2)) (list-union (registers-modified seq1) (registers-modified seq2)) (append (statements seq1) (statements seq2)))) | function parallel_instruction_sequences(seq1, seq2) { return make_instruction_sequence( list_union(registers_needed(seq1), registers_needed(seq2)), list_union(registers_modified(seq1), registers_modified(seq2)), append(instructions(seq1), instructions(seq2))); } |