16. The Sparrowhawk Subset

Part of the Hawk Manual
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

Contents

16.1. The Subset
16.2. Code Conversion
16.2.1. Converting LIL
16.2.2. Converting Memory Reference Instructions


16.1. The Subset

Strictly speaking, many of the instructions in the Hawk instruction set are unnecessary. Omitting some of these instructions can greatly simplify the hardware.

The Sparrowhawk subset of the Hawk instruction set omits all 32-bit instruciton formats. Specifically, there are no long memory reference instructions (see Chapter 3), no long immediate instructions (see Chapter 4), and no coprocessor instructions (see Section 11.4).

Use of the opcodes assigned to these instructions on a Sparrowhawk processor results in an instruction trap (see Section 13.1. Operating systems on the Sparrowhawk may virtualize the missing instructions using an appropriate trap service routine, allowing Sparrowhawk processors to run aribtrary code written for the Hawk.

16.2. Code Conversion

Most Hawk code can be converted to the Sparrowhawk subset by making local substitutions that replace single Hawk instrucitons with sequences of Sparrowhawk instructions that do the same thing. In some cases, this requires use of at most one temporary register; in these cases, if no register is available, additional changes to the original code are required.

Macro assemblers supporting the Sparrowhawk should provide macros for all of the Hawk instructions in the header file sparrowhawk.h. This provides a second avenue to virtualize the Hawk instruciton set on the Sparrowhawk.

Note that many of the substitutions needed to convert Hawk code to Sparrowhawk code occupy more memory. As a result, code that assembles correctly on the Hawk will not always assemble correctly with these substitutions. The reason is that the expanded code may push some PC-relative addresses out of bounds.

16.2.1. Converting LIL

For LIL (see Chapter 4), replace:

        LIL     R5,CONST

With LIS followed by an zero or more ORIS instructions (see Section 5.2). In the general case, this gives:

        LIS     R5,(CONST>>16)
        ORIS    R5,(CONST>> 8)&#FF
        ORIS    R5,(CONST    )&#FF

If the constant can be represented in 16 bits, this code suffices:

        LIS     R5,(CONST>>8)
        ORIS    R5,(CONST   )&#FF

Note that the above substitutions may be problematic when the constant being loaded is a value that will be modified by the linker when combining separately processed object files. For example, when the constant is used in one object file but defined in another. In this case, the best workable substitution involves replacing LIL R5,const with something like the following:

        LOAD    R5,WCONST
        ...
WCONST: W       CONST

In the above, note the following:

16.2.2. Converting Memory Reference Instructions

For memory reference instructions, a register must be used for effective address computation. Here, we will use r[1] as a temporary because this register is commonly used for subroutine linkage and therefore routinely unsafe to use for long-term operand storage.

Taking STORE as the prototypical memory reference instruction (see Chapter 3), we can replace:

        STORE   R5,R2,DISP

With an appropriate load immediate sequence (see Chapter 5) followed by an add (see Section 11.3) prior to storing the operand. In the general case, this gives:

        LIS     R1,(DISP>>8)
        ORIS    R1,(DISP   )&#FF
        PLUS    R1,R2           ; compute the effective address
        STORES  R5,R1           ; store in the effective address

Note the use of PLUS to compute the effective address instead of ADD (see Section 8.2). This is needed here because STORE does not change the condition codes.

Note that if the 16-bit displacement DISP is modified by the linker, the above substitution will not work. The solution to the same problem given for LIL in the previous section also works here.

The above substitution can be made for most memory reference instructions, but many optimizations are possible. When the displacement is short, the ORIS instruction may be omitted. In the case of LOAD and LEA, the destination register may be used for effective address computation. So, we can replace:

        LOAD    R5,R2,100

With the optimized sequence:

        LIS     R5,100
        PLUS    R5,R2
        LOADS   R5,R5

PC relative addressing requires only small changes. We can replace:

        LOAD    R5,LOC

With an a similar instruction sequence:

        LIS     R5,((LOC-ALOADS)>>8)
        ORIS    R5,((LOC-ALOADS)   )&#FF
        PLUS    R5,PC
ALOADS: LOADS   R5,R5
The tricky detail in the above is the placemet of the label ALOADS. Note that the program counter is incremented immediately upon instruction fetch, before the instruction is executed. Therefore, the value used by PLUS will be the address of the immediately following LOADS. As a result, the computed address (LOC-ALOADS)+PC will be (LOC-ALOADS)+ which is LOC.