You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sicp-all-tasks/sicp/5_002e38

67 lines
3.3 KiB

Exercise 5.38: Our compiler is clever about
avoiding unnecessary stack operations, but it is not clever at all when it
comes to compiling calls to the primitive procedures of the language in terms
of the primitive operations supplied by the machine. For example, consider how
much code is compiled to compute (+ a 1): The code sets up an argument
list in argl, puts the primitive addition procedure (which it finds by
looking up the symbol + in the environment) into proc, and tests
whether the procedure is primitive or compound. The compiler always generates
code to perform the test, as well as code for primitive and compound branches
(only one of which will be executed). We have not shown the part of the
controller that implements primitives, but we presume that these instructions
make use of primitive arithmetic operations in the machine’s data paths.
Consider how much less code would be generated if the compiler could
open-code primitives—that is, if it could generate code to directly
use these primitive machine operations. The expression (+ a 1) might be
compiled into something as simple as328
(assign val (op lookup-variable-value)
(const a)
(reg env))
(assign val (op +)
(reg val)
(const 1))
In this exercise we will extend our compiler to support open coding of selected
primitives. Special-purpose code will be generated for calls to these
primitive procedures instead of the general procedure-application code. In
order to support this, we will augment our machine with special argument
registers arg1 and arg2. The primitive arithmetic operations of
the machine will take their inputs from arg1 and arg2. The
results may be put into val, arg1, or arg2.
The compiler must be able to recognize the application of an open-coded
primitive in the source program. We will augment the dispatch in the
compile procedure to recognize the names of these primitives in addition
to the reserved words (the special forms) it currently
recognizes.329 For each special
form our compiler has a code generator. In this exercise we will construct a
family of code generators for the open-coded primitives.
The open-coded primitives, unlike the special forms, all need their operands
evaluated. Write a code generator spread-arguments for use by all the
open-coding code generators. Spread-arguments should take an operand
list and compile the given operands targeted to successive argument registers.
Note that an operand may contain a call to an open-coded primitive, so argument
registers will have to be preserved during operand evaluation.
For each of the primitive procedures =, *, -, and
+, write a code generator that takes a combination with that operator,
together with a target and a linkage descriptor, and produces code to spread
the arguments into the registers and then perform the operation targeted to the
given target with the given linkage. You need only handle expressions with two
operands. Make compile dispatch to these code generators.
Try your new compiler on the factorial example. Compare the resulting
code with the result produced without open coding.
Extend your code generators for + and * so that they can handle
expressions with arbitrary numbers of operands. An expression with more than
two operands will have to be compiled into a sequence of operations, each with
only two inputs.