TC-7 Samples
The goal of TC-7 is straightforward: starting from
LIR, generate the MIPS instructions, except that you don’t
have actual registers: we still heavily use Temp
s. Register
allocation will be done in a later stage, TC-9, Register Allocation.
let
var answer := 42
in
answer := 51
end
$ tc --inst-display the-answer.tig
# == Final assembler ouput. == #
# Routine: _main
tc_main:
# Allocate frame
move $x11, $ra
move $x3, $s0
move $x4, $s1
move $x5, $s2
move $x6, $s3
move $x7, $s4
move $x8, $s5
move $x9, $s6
move $x10, $s7
l0:
li $x1, 42
sw $x1, ($fp)
li $x2, 51
sw $x2, ($fp)
l1:
move $s0, $x3
move $s1, $x4
move $s2, $x5
move $s3, $x6
move $s4, $x7
move $s5, $x8
move $s6, $x9
move $s7, $x10
move $ra, $x11
# Deallocate frame
jr $ra
$ echo $?
0
At this stage the compiler cannot know what registers are used; that’s
why in the previous output it saves “uselessly” all the callee-save
registers on main
entry. For the same reason, the frame is not
allocated.
While Nolimips accepts the lack of register allocation, it does require
the frame to be allocated. That is the purpose of
--nolimips-display
:
$ tc --nolimips-display the-answer.tig
# == Final assembler ouput. == #
# Routine: _main
tc_main:
sw $fp, -4 ($sp)
move $fp, $sp
sub $sp, $sp, 8
move $x11, $ra
move $x3, $s0
move $x4, $s1
move $x5, $s2
move $x6, $s3
move $x7, $s4
move $x8, $s5
move $x9, $s6
move $x10, $s7
l0:
li $x1, 42
sw $x1, ($fp)
li $x2, 51
sw $x2, ($fp)
l1:
move $s0, $x3
move $s1, $x4
move $s2, $x5
move $s3, $x6
move $s4, $x7
move $s5, $x8
move $s6, $x9
move $s7, $x10
move $ra, $x11
move $sp, $fp
lw $fp, -4 ($fp)
jr $ra
$ echo $?
0
The final stage, register allocation, addresses both issues. For your information, it results in:
$ tc -sI the-answer.tig
# == Final assembler ouput. == #
# Routine: _main
tc_main:
sw $fp, -4 ($sp)
move $fp, $sp
sub $sp, $sp, 8
l0:
li $t0, 42
sw $t0, ($fp)
li $t0, 51
sw $t0, ($fp)
l1:
move $sp, $fp
lw $fp, -4 ($fp)
jr $ra
$ 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 --inst-display add.tig
# == Final assembler ouput. == #
# Routine: add
tc_l0:
# Allocate frame
move $x16, $ra
sw $a0, ($fp)
move $x0, $a1
move $x1, $a2
move $x8, $s0
move $x9, $s1
move $x10, $s2
move $x11, $s3
move $x12, $s4
move $x13, $s5
move $x14, $s6
move $x15, $s7
l2:
add $x7, $x0, $x1
move $v0, $x7
l3:
move $s0, $x8
move $s1, $x9
move $s2, $x10
move $s3, $x11
move $s4, $x12
move $s5, $x13
move $s6, $x14
move $s7, $x15
move $ra, $x16
# Deallocate frame
jr $ra
.data
l1:
.word 1
.asciiz "\n"
.text
# Routine: _main
tc_main:
# Allocate frame
move $x29, $ra
move $x21, $s0
move $x22, $s1
move $x23, $s2
move $x24, $s3
move $x25, $s4
move $x26, $s5
move $x27, $s6
move $x28, $s7
l4:
move $x5, $fp
move $a0, $fp
li $x17, 2
move $a1, $x17
li $x18, 3
move $a2, $x18
jal tc_l0
move $x4, $v0
move $a0, $x5
li $x19, 1
move $a1, $x19
move $a2, $x4
jal tc_l0
move $x6, $v0
move $a0, $x6
jal tc_print_int
la $x20, l1
move $a0, $x20
jal tc_print
l5:
move $s0, $x21
move $s1, $x22
move $s2, $x23
move $s3, $x24
move $s4, $x25
move $s5, $x26
move $s6, $x27
move $s7, $x28
move $ra, $x29
# Deallocate frame
jr $ra
$ 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 --inst-display many-args.tig
# == Final assembler ouput. == #
# Routine: many
tc_l0:
# Allocate frame
move $x30, $ra
sw $a0, ($fp)
move $x0, $a1
move $x1, $a2
move $x2, $a3
lw $x3, 4 ($fp)
lw $x4, 8 ($fp)
lw $x5, 12 ($fp)
lw $x6, 16 ($fp)
lw $x7, 20 ($fp)
lw $x8, 24 ($fp)
lw $x9, 28 ($fp)
move $x22, $s0
move $x23, $s1
move $x24, $s2
move $x25, $s3
move $x26, $s4
move $x27, $s5
move $x28, $s6
move $x29, $s7
l2:
add $x13, $x0, $x1
add $x14, $x13, $x2
add $x15, $x14, $x3
add $x16, $x15, $x4
add $x17, $x16, $x5
add $x18, $x17, $x6
add $x19, $x18, $x7
add $x20, $x19, $x8
add $x21, $x20, $x9
move $v0, $x21
l3:
move $s0, $x22
move $s1, $x23
move $s2, $x24
move $s3, $x25
move $s4, $x26
move $s5, $x27
move $s6, $x28
move $s7, $x29
move $ra, $x30
# Deallocate frame
jr $ra
.data
l1:
.word 1
.asciiz "\n"
.text
# Routine: _main
tc_main:
# Allocate frame
move $x50, $ra
move $x42, $s0
move $x43, $s1
move $x44, $s2
move $x45, $s3
move $x46, $s4
move $x47, $s5
move $x48, $s6
move $x49, $s7
l4:
move $a0, $fp
li $x31, 0
move $a1, $x31
li $x32, 1
move $a2, $x32
li $x33, 2
move $a3, $x33
li $x34, 3
sw $x34, 4 ($sp)
li $x35, 4
sw $x35, 8 ($sp)
li $x36, 5
sw $x36, 12 ($sp)
li $x37, 6
sw $x37, 16 ($sp)
li $x38, 7
sw $x38, 20 ($sp)
li $x39, 8
sw $x39, 24 ($sp)
li $x40, 9
sw $x40, 28 ($sp)
jal tc_l0
move $x12, $v0
move $a0, $x12
jal tc_print_int
la $x41, l1
move $a0, $x41
jal tc_print
l5:
move $s0, $x42
move $s1, $x43
move $s2, $x44
move $s3, $x45
move $s4, $x46
move $s5, $x47
move $s6, $x48
move $s7, $x49
move $ra, $x50
# Deallocate frame
jr $ra
$ echo $?
0
Once your function calls work properly, you can start using Nolimips
(using options --nop-after-branch --unlimited-registers
--execute
) to check the behavior of your compiler.
$ tc -eR --nolimips-display add.tig > add.nolimips
$ echo $?
0
$ nolimips -l nolimips -Nue add.nolimips
6
$ echo $?
0
You must also complete the runtime. No difference must be observable between a run with HAVM and another with Nolimips:
substring("", 1, 1)
$ tc -e --nolimips-display substring-0-1-1.tig
# == Final assembler ouput. == #
.data
l0:
.word 0
.asciiz ""
.text
# Routine: _main
tc_main:
# Allocate frame
move $x12, $ra
move $x4, $s0
move $x5, $s1
move $x6, $s2
move $x7, $s3
move $x8, $s4
move $x9, $s5
move $x10, $s6
move $x11, $s7
l1:
la $x1, l0
move $a0, $x1
li $x2, 1
move $a1, $x2
li $x3, 1
move $a2, $x3
jal tc_substring
l2:
move $s0, $x4
move $s1, $x5
move $s2, $x6
move $s3, $x7
move $s4, $x8
move $s5, $x9
move $s6, $x10
move $s7, $x11
move $ra, $x12
# Deallocate frame
jr $ra
$ echo $?
0
$ tc -eR --nolimips-display substring-0-1-1.tig > substring-0-1-1.nolimips
$ echo $?
0
$ nolimips -l nolimips -Nue substring-0-1-1.nolimips
substring: arguments out of bounds
$ echo $?
120