TC-X Samples
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 another stage,
TC-9, Register Allocation.
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"
$ echo $?
0
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"
$ echo $?
0
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), %t11
movl %t11, (%ebp)
movl 16(%ebp), %t0
movl 20(%ebp), %t1
movl %ebx, %t8
movl %edi, %t9
movl %esi, %t10
l2:
movl %t0, %t7
addl %t1, %t7
movl %t7, %eax
l3:
movl %t8, %ebx
movl %t9, %edi
movl %t10, %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, %t16
movl %edi, %t17
movl %esi, %t18
l4:
movl %ebp, %t5
movl $3, %t12
pushl %t12
movl $2, %t13
pushl %t13
pushl %ebp
call tc_l0
movl %eax, %t4
pushl %t4
movl $1, %t14
pushl %t14
pushl %t5
call tc_l0
movl %eax, %t6
pushl %t6
call tc_print_int
lea l1, %t15
pushl %t15
call tc_print
l5:
movl %t16, %ebx
movl %t17, %edi
movl %t18, %esi
# Deallocate frame
ret $0
l7:
.size tc_main,l7-tc_main
.ident "LRDE Tiger Compiler"
$ echo $?
0
You must be able to handle functions with any number of arguments:
let
function many(a0 : int, a1 : int, a2 : int, a3 : int, a4 : int,
a5 : int, a6 : int, a7 : int, a8 : int, a9 : int): int =
a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9
in
print_int(many(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
print("\n")
end
$ tc -e --target-ia32 --inst-display many-args-ia32.tig
/** Tiger final assembler ouput. */
/** Routine: many */
.text
.globl tc_l0
.type tc_l0,@function
tc_l0:
# Allocate frame
movl 12(%ebp), %t25
movl %t25, (%ebp)
movl 16(%ebp), %t0
movl 20(%ebp), %t1
movl 24(%ebp), %t2
movl 28(%ebp), %t3
movl 32(%ebp), %t4
movl 36(%ebp), %t5
movl 40(%ebp), %t6
movl 44(%ebp), %t7
movl 48(%ebp), %t8
movl 52(%ebp), %t9
movl %ebx, %t22
movl %edi, %t23
movl %esi, %t24
l2:
movl %t0, %t13
addl %t1, %t13
movl %t13, %t14
addl %t2, %t14
movl %t14, %t15
addl %t3, %t15
movl %t15, %t16
addl %t4, %t16
movl %t16, %t17
addl %t5, %t17
movl %t17, %t18
addl %t6, %t18
movl %t18, %t19
addl %t7, %t19
movl %t19, %t20
addl %t8, %t20
movl %t20, %t21
addl %t9, %t21
movl %t21, %eax
l3:
movl %t22, %ebx
movl %t23, %edi
movl %t24, %esi
# Deallocate frame
ret $44
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, %t37
movl %edi, %t38
movl %esi, %t39
l4:
movl $9, %t26
pushl %t26
movl $8, %t27
pushl %t27
movl $7, %t28
pushl %t28
movl $6, %t29
pushl %t29
movl $5, %t30
pushl %t30
movl $4, %t31
pushl %t31
movl $3, %t32
pushl %t32
movl $2, %t33
pushl %t33
movl $1, %t34
pushl %t34
movl $0, %t35
pushl %t35
pushl %ebp
call tc_l0
movl %eax, %t12
pushl %t12
call tc_print_int
lea l1, %t36
pushl %t36
call tc_print
l5:
movl %t37, %ebx
movl %t38, %edi
movl %t39, %esi
# Deallocate frame
ret $0
l7:
.size tc_main,l7-tc_main
.ident "LRDE Tiger Compiler"
$ echo $?
0
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"
$ echo $?
0
$ 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"
$ echo $?
0
$ tc -e --target-ia32 --asm-display substring-0-1-1-ia32.tig > substring-0-1-1-ia32.s
$ echo $?
0
$ gcc -m32 -osubstring-0-1-1-ia32 substring-0-1-1-ia32.s
/usr/bin/ld: /tmp/ccj5QnUo.o: warning: relocation in read-only section `.text'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
$ echo $?
0
$ ./substring-0-1-1-ia32
substring: arguments out of bounds
$ echo $?
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"
$ echo $?
0
$ 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"
$ echo $?
0