Hardware Reference
In-Depth Information
For example, consider the following code sequence, where &; returns the address of a vari-
able and * dereferences a pointer:
p = &a-- gets address of a in p
a = ...-- assigns to a directly
*p = ...-- uses p to assign to a
...a...-- accesses a
The variable a could not be register allocated across the assignment to *p without generating
incorrect code. Aliasing causes a substantial problem because it is often difficult or impossible
to decide what objects a pointer may refer to. A compiler must be conservative; some com-
pilers will not allocate any local variables of a procedure in a register when there is a pointer
that may refer to one of the local variables.
How The Architect Can Help The Compiler Writer
Today, the complexity of a compiler does not come from translating simple statements like A
= B + C. Most programs are locally simple , and simple translations work fine. Rather, complex-
ity arises because programs are large and globally complex in their interactions, and because
the structure of compilers means decisions are made one step at a time about which code se-
quence is best.
Compiler writers often are working under their own corollary of a basic principle in archi-
tecture: Make the frequent cases fast and the rare case correct . That is, if we know which cases are
frequent and which are rare, and if generating code for both is straightforward, then the qual-
ity of the code for the rare case may not be very important—but it must be correct!
Some instruction set properties help the compiler writer. These properties should not be
thought of as hard-and-fast rules, but rather as guidelines that will make it easier to write a
compiler that will generate efficient and correct code.
Provide regularity —Whenever it makes sense, the three primary components of an instruc-
tion set—the operations, the data types, and the addressing modes—should be orthogon-
al . Two aspects of an architecture are said to be orthogonal if they are independent. For
example, the operations and addressing modes are orthogonal if, for every operation to
which one addressing mode can be applied, all addressing modes are applicable. This reg-
ularity helps simplify code generation and is particularly important when the decision
about what code to generate is split into two passes in the compiler. A good counterex-
ample of this property is restricting what registers can be used for a certain class of in-
structions. Compilers for special-purpose register architectures typically get stuck in this
dilemma. This restriction can result in the compiler finding itself with lots of available re-
gisters, but none of the right kind!
Provide primitives, not solutions —Special features that “match” a language construct or a
kernel function are often unusable. Atempts to support high-level languages may work
only with one language or do more or less than is required for a correct and efficient im-
plementation of the language. An example of how such atempts have failed is given in
Section A.10 .
Simplify trade-offs among alternatives —One of the toughest jobs a compiler writer has is ig-
uring out what instruction sequence will be best for every segment of code that arises. In
earlier days, instruction counts or total code size might have been good metrics, but—as
we saw in Chapter 1 —this is no longer true. With caches and pipelining, the trade-offs
have become very complex. Anything the designer can do to help the compiler writer un-
derstand the costs of alternative code sequences would help improve the code. One of the
Search WWH ::




Custom Search