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.

the-answer.tig
let
  var answer := 42
in
  answer := 51
end
tc --inst-display the-answer.tig
$ 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
$ 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
$ 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:

add.tig
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
$ 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:

many-args.tig
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
$ 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
$ tc -eR --nolimips-display add.tig > add.nolimips

$ echo $?
0
nolimips -l nolimips -Nue add.nolimips
$ 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-0-1-1.tig
substring("", 1, 1)
tc -e --nolimips-display substring-0-1-1.tig
$ 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
$ 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
$ nolimips -l nolimips -Nue substring-0-1-1.nolimips
substring: arguments out of bounds
$ echo $?
120