Re: porting a compiler to a new architecture

"Jan Gray" <jsgray@acm.org>
22 Mar 1999 01:11:46 -0500

          From comp.compilers

Related articles
porting a compiler to a new architecture mathai@ecf.toronto.edu (1999-03-09)
Re: porting a compiler to a new architecture albaugh@agames.com (1999-03-10)
Re: porting a compiler to a new architecture jsgray@acm.org (Jan Gray) (1999-03-22)
Re: porting a compiler to a new architecture hawa@celsiustech.se (Hans Walheim) (1999-03-22)
Re: porting a compiler to a new architecture nr@labrador.cs.virginia.edu (Norman Ramsey) (1999-03-28)
| List of all articles for this month |

From: "Jan Gray" <jsgray@acm.org>
Newsgroups: comp.compilers
Date: 22 Mar 1999 01:11:46 -0500
Organization: self
References: 99-03-036
Keywords: architecture, optimize

mathai@ecf.toronto.edu wrote ...
>I'm designing a RISC CPU for fun, and wanted to know how difficult it
>would be to port a compiler like gcc to generate code (non-optimized is
>fine) for my machine? I'm ignorant of compilers, but can learn .. I'd just
>like to assess how difficult it would be.
>
>Thanks
>[One RISC is pretty much like another for a straightforward code generator,
>so I'd think you should be able to get a limping version of lcc in a week
>or two. -John]


I recently designed another RISC CPU for fun. This one is a 16-bit
16-register classic pipelined RISC, implemented using Xilinx Student Ed.,
occupying about 60% of a Xilinx XC4005XL-3 FPGA, running at 25 MHz.


I targeted lcc 4.1 to this new processor and was quite pleased with the
process and the results. Most of the work was done in one long and very
pleasant day. However, I had already read Fraser and Hanson [1], and I used
to write compilers. So your mileage may vary.


(I look forward to trying the same thing with GCC.)




Here's what I did. I downloaded lcc4.1 from
www.cs.princeton.edu/software/lcc and built it with MS VC++. I read
doc/4.html, "The lcc 4.1 Code-Generation Interface".


Since I was targeting a classic RISC instruction set architecture, I started
with the MIPS machine description, mips.md. I copied that to xr16.md, added
xr16.md to the makefile, and added two lines to bind.c for "-target=xr16".


Next I designed xr16 register conventions (below) and modified my md to
target these registers.


Register Use
r0 always zero
r1 reserved for assembler
r2 function return value
r3-r5 function arguments
r6-r9 temporaries
r10-r13 register variables
r14 stack pointer (sp)
r15 return address


At this point I had a C compiler for a 32-bit 16-register RISC. But I
needed to target a 16-bit machine with sizeof(int)=sizeof(void*)=2. No
problem! Lcc obtains target operand sizes from md tables -- so, change some
4's to 2's:


Interface xr16IR = {
                1, 1, 0, /* char */
                2, 2, 0, /* short */
                2, 2, 0, /* int */
                4, 4, 0, /* long */
                ...
                2, 2, 0, /* T * */
                ...


Next I built and ran the "ops" utility "ops c=1 s=2 i=2 l=4 h=4 f=4 d=4 x=4
p=2" which prints the required primitive dag operator set for a machine with
2-byte ints and pointers. I modified my instruction templates to cover this
operator set, and removed some 32-bit int assumptions inherited from
mips.md.


(This small effort compared extremely favorably with past experience of
porting another C/C++ compiler from 16-bit ints/pointers to 32-bit
ints/pointers.)


I decided to keep long ints in register pairs. This required just four
lines of code to introduce a new class of register resource and to target
the 4-byte integer types to that resource. Wow. (Although this hasn't had
much testing yet.)


Compared to the MIPS templates, my xr16 templates reflect
* different branch scheme (condition codes, sorry);
* logic/shift instructions are (necessarily) two-operand only;
* immediate constants are encoded differently;
* mul, div, and rem are calls upon helper routines.


All totaled, in xr16.md, I replaced the entire dag operator set (entirely
generated by ops utility), changed about 200 lines of instruction templates
(mostly to reflect 2-byte int/pointer changes in dag operator types), and
about 100 lines of miscellaneous code.


I have yet to properly diagnose use of floating point (never to be
implemented). And except for some test apps, I haven't properly tested the
compiler.


The resulting code quality? Not superlative, but not too bad. For example,
for this source:


typedef struct TN {
    int k;
    struct TN *left, *right;
} *T;


T search(int k, T p) {
    while (p && p->k != k)
        if (p->k < k)
            p = p->right;
        else
            p = p->left;
    return p;
}


my "lcc -S -Wf-target=xr16 search.c" emits this assembler:


align 16
search:
br L3
L2:
lw r9,(r4)
cmp r9,r3
bge L5
lw r4,4(r4)
br L6
L5:
lw r4,2(r4)
L6:
L3:
mov r9,r4
cmp r9,r0
beq L7
lw r9,(r4)
cmp r9,r3
bne L2
L7:
mov r2,r4
L1:
ret


which I consider quite acceptable for my purposes.




I only wish there was a retargetable assembler as easy to port as is lcc. I
looked at gas but it seemed rather large. Any pointers? Instead I wrote a
simple two-pass assembler from scratch. This took twice as long as the
compiler port...


Jan Gray
www3.sympatico.ca/jsgray/homebrew.htm




[1] Christopher W. Fraser, David R. Hanson. A Retargetable C Compiler :
Design and Implementation. Addison-Wesley. ISBN: 0805316701


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.