Next: TC-X Given Code, Previous: TC-X Goals, Up: TC-X [Contents][Index]
The goal of TC-X is straightforward: starting from
LIR, generate the IA-32 instructions, except that you
don’t have actual registers: we still heavily use Temp
s.
Register allocation has been (or will be) done in a another stage,
TC-9.
let var answer := 42 in answer := 51 end
$ tc --target-ia32 --inst-display the-answer-ia32.tig /** Tiger final assembler ouput. */ /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: # Allocate frame movl %ebx, %t3 movl %edi, %t4 movl %esi, %t5 l0: movl $42, %t1 movl %t1, (%ebp) movl $51, %t2 movl %t2, (%ebp) l1: movl %t3, %ebx movl %t4, %edi movl %t5, %esi # Deallocate frame ret $0 l2: .size tc_main,l2-tc_main .ident "LRDE Tiger Compiler"
At this stage the compiler cannot know what registers are used; the frame is not allocated. The final stage, register allocation, addresses this issue. For your information, it results in:
$ tc --target-ia32 -sI the-answer-ia32.tig /** Tiger final assembler ouput. */ /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: pushl %ebp subl $4, %esp movl %esp, %ebp subl $4, %esp l0: movl $42, %ecx movl %ecx, (%ebp) movl $51, %ecx movl %ecx, (%ebp) l1: addl $4, %ebp leave ret $0 l2: .size tc_main,l2-tc_main .ident "LRDE Tiger Compiler"
A delicate part of this exercise is handling the function calls:
let function add(x: int, y: int) : int = x + y in print_int(add(1,(add(2, 3)))); print("\n") end
$ tc -e --target-ia32 --inst-display add-ia32.tig /** Tiger final assembler ouput. */ /** Routine: add */ .text .globl tc_l0 .type tc_l0,@function tc_l0: # Allocate frame movl 12(%ebp), %t10 movl %t10, (%ebp) movl 16(%ebp), %t0 movl 20(%ebp), %t1 movl %ebx, %t7 movl %edi, %t8 movl %esi, %t9 l2: movl %t0, %t6 addl %t1, %t6 movl %t6, %eax l3: movl %t7, %ebx movl %t8, %edi movl %t9, %esi # Deallocate frame ret $12 l6: .size tc_l0,l6-tc_l0 .section .rodata l1: .long 1 .asciz "\n" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: # Allocate frame movl %ebx, %t15 movl %edi, %t16 movl %esi, %t17 l4: movl $3, %t11 pushl %t11 movl $2, %t12 pushl %t12 pushl %ebp call tc_l0 movl %eax, %t4 pushl %t4 movl $1, %t13 pushl %t13 pushl %ebp call tc_l0 movl %eax, %t5 pushl %t5 call tc_print_int lea l1, %t14 pushl %t14 call tc_print l5: movl %t15, %ebx movl %t16, %edi movl %t17, %esi # Deallocate frame ret $0 l7: .size tc_main,l7-tc_main .ident "LRDE Tiger Compiler"
Once your compiler is complete, you can produce an actual IA-32
output, assemble it and link it with gcc
to produce a real
executable program:
$ tc -e --target-ia32 --asm-compute --inst-display add-ia32.tig /** Tiger final assembler ouput. */ /** Routine: add */ .text .globl tc_l0 .type tc_l0,@function tc_l0: pushl %ebp subl $4, %esp movl %esp, %ebp subl $4, %esp movl 12(%ebp), %ecx movl %ecx, (%ebp) movl 16(%ebp), %eax movl 20(%ebp), %ecx l2: addl %ecx, %eax l3: addl $4, %ebp leave ret $12 l6: .size tc_l0,l6-tc_l0 .section .rodata l1: .long 1 .asciz "\n" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: pushl %ebp subl $4, %esp movl %esp, %ebp subl $0, %esp l4: movl $3, %ecx pushl %ecx movl $2, %ecx pushl %ecx pushl %ebp call tc_l0 pushl %eax movl $1, %ecx pushl %ecx pushl %ebp call tc_l0 pushl %eax call tc_print_int lea l1, %ecx pushl %ecx call tc_print l5: addl $4, %ebp leave ret $0 l7: .size tc_main,l7-tc_main .ident "LRDE Tiger Compiler"
$ tc -e --target-ia32 --asm-display add-ia32.tig >add-ia32.s
$ gcc -m32 -oadd-ia32 add-ia32.s
$ ./add-ia32 6
The runtime must be functional. No difference must be observable in comparison with a run with HAVM:
substring("", 1, 1)
$ tc -e --target-ia32 --inst-display substring-0-1-1-ia32.tig /** Tiger final assembler ouput. */ .section .rodata l0: .long 0 .asciz "" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: # Allocate frame movl %ebx, %t4 movl %edi, %t5 movl %esi, %t6 l1: movl $1, %t1 pushl %t1 movl $1, %t2 pushl %t2 lea l0, %t3 pushl %t3 call tc_substring l2: movl %t4, %ebx movl %t5, %edi movl %t6, %esi # Deallocate frame ret $0 l3: .size tc_main,l3-tc_main .ident "LRDE Tiger Compiler"
$ tc -e --target-ia32 --asm-compute --inst-display substring-0-1-1-ia32.tig /** Tiger final assembler ouput. */ .section .rodata l0: .long 0 .asciz "" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: pushl %ebp subl $4, %esp movl %esp, %ebp subl $0, %esp l1: movl $1, %ecx pushl %ecx movl $1, %ecx pushl %ecx lea l0, %ecx pushl %ecx call tc_substring l2: addl $4, %ebp leave ret $0 l3: .size tc_main,l3-tc_main .ident "LRDE Tiger Compiler"
$ tc -e --target-ia32 --asm-display substring-0-1-1-ia32.tig >substring-0-1-1-ia32.s
$ gcc -m32 -osubstring-0-1-1-ia32 substring-0-1-1-ia32.s
$ ./substring-0-1-1-ia32 error→substring: arguments out of bounds ⇒120
The following example illustrates conditional jumps.
if 42 > 51 then "forty-two" else "fifty-one"
$ tc -e --target-ia32 --inst-display condjump-ia32.tig /** Tiger final assembler ouput. */ .section .rodata l0: .long 9 .asciz "forty-two" .section .rodata l1: .long 9 .asciz "fifty-one" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: # Allocate frame movl %ebx, %t4 movl %edi, %t5 movl %esi, %t6 l5: movl $42, %t1 cmp $51, %t1 jg l2 l3: lea l1, %t2 l4: jmp l6 l2: lea l0, %t3 jmp l4 l6: movl %t4, %ebx movl %t5, %edi movl %t6, %esi # Deallocate frame ret $0 l7: .size tc_main,l7-tc_main .ident "LRDE Tiger Compiler"
$ tc -e --target-ia32 --asm-compute --inst-display condjump-ia32.tig /** Tiger final assembler ouput. */ .section .rodata l0: .long 9 .asciz "forty-two" .section .rodata l1: .long 9 .asciz "fifty-one" /** Routine: _main */ .text .globl tc_main .type tc_main,@function tc_main: pushl %ebp subl $4, %esp movl %esp, %ebp subl $0, %esp l5: movl $42, %ecx cmp $51, %ecx jg l2 l3: lea l1, %ecx l4: jmp l6 l2: lea l0, %ecx jmp l4 l6: addl $4, %ebp leave ret $0 l7: .size tc_main,l7-tc_main .ident "LRDE Tiger Compiler"
Next: TC-X Given Code, Previous: TC-X Goals, Up: TC-X [Contents][Index]