TC-Y Samples

The goal of TC-Y is straightforward: starting from LIR, generate the ARM 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.

the-answer-arm.tig
let
  var answer := 42
in
  answer := 51
end
tc --target-arm --inst-display the-answer-arm.tig
$ tc --target-arm --inst-display the-answer-arm.tig
# Tiger final assembler ouput.

# Routine: _main
.global tc_main
.text
tc_main:
# Allocate frame
	mov	t3, r10
	mov	t4, r4
	mov	t5, r5
	mov	t6, r6
	mov	t7, r7
	mov	t8, r8
	mov	t9, r9
l0:
	ldr	t1, =42
	str	t1, [fp, #0]
	ldr	t2, =51
	str	t2, [fp, #0]
l1:
	mov	r10, t3
	mov	r4, t4
	mov	r5, t5
	mov	r6, t6
	mov	r7, t7
	mov	r8, t8
	mov	r9, t9
# Deallocate frame
	pop	{fp, pc}

.ltorg
$ 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-arm -sI the-answer-arm.tig
$ tc --target-arm -sI the-answer-arm.tig
# Tiger final assembler ouput.

# Routine: _main
.global tc_main
.text
tc_main:
	push	{fp, lr}
	sub	fp, sp, #4
	sub	sp, sp, #4
l0:
	ldr	r1, =42
	str	r1, [fp, #0]
	ldr	r1, =51
	str	r1, [fp, #0]
l1:
	add	sp, sp, #4
	pop	{fp, pc}

.ltorg
$ echo $?
0

A delicate part of this exercise is handling the function calls:

add-arm.tig
let
  function add(x: int, y: int) : int = x + y
in
  print_int(add(1,(add(2, 3)))); print("\n")
end
tc -e --target-arm --inst-display add-arm.tig
$ tc -e --target-arm --inst-display add-arm.tig
# Tiger final assembler ouput.

# Routine: add
.global tc_l0
.text
tc_l0:
# Allocate frame
	str	r1, [fp, #0]
	mov	t0, r2
	mov	t1, r3
	mov	t8, r10
	mov	t9, r4
	mov	t10, r5
	mov	t11, r6
	mov	t12, r7
	mov	t13, r8
	mov	t14, r9
l2:
	add	t7, t0, t1
	mov	r0, t7
l3:
	mov	r10, t8
	mov	r4, t9
	mov	r5, t10
	mov	r6, t11
	mov	r7, t12
	mov	r8, t13
	mov	r9, t14
# Deallocate frame
	pop	{fp, pc}

.ltorg

.data
l1:
	.word 1
	.asciz "\n"

# Routine: _main
.global tc_main
.text
tc_main:
# Allocate frame
	mov	t19, r10
	mov	t20, r4
	mov	t21, r5
	mov	t22, r6
	mov	t23, r7
	mov	t24, r8
	mov	t25, r9
l4:
	mov	t5, fp
	mov	r1, fp
	ldr	t15, =2
	mov	r2, t15
	ldr	t16, =3
	mov	r3, t16
	bl	tc_l0
	mov	t4, r0
	mov	r1, t5
	ldr	t17, =1
	mov	r2, t17
	mov	r3, t4
	bl	tc_l0
	mov	t6, r0
	mov	r1, t6
	bl	tc_print_int
	ldr	t18, =l1
	mov	r1, t18
	bl	tc_print
l5:
	mov	r10, t19
	mov	r4, t20
	mov	r5, t21
	mov	r6, t22
	mov	r7, t23
	mov	r8, t24
	mov	r9, t25
# Deallocate frame
	pop	{fp, pc}

.ltorg
$ echo $?
0

You must be able to handle functions with any number of arguments:

many-args-arm.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 --target-arm --inst-display many-args-arm.tig
$ tc -e --target-arm --inst-display many-args-arm.tig
# Tiger final assembler ouput.

# Routine: many
.global tc_l0
.text
tc_l0:
# Allocate frame
	str	r1, [fp, #0]
	mov	t0, r2
	mov	t1, r3
	ldr	t2, [fp, #12]
	ldr	t3, [fp, #16]
	ldr	t4, [fp, #20]
	ldr	t5, [fp, #24]
	ldr	t6, [fp, #28]
	ldr	t7, [fp, #32]
	ldr	t8, [fp, #36]
	ldr	t9, [fp, #40]
	mov	t22, r10
	mov	t23, r4
	mov	t24, r5
	mov	t25, r6
	mov	t26, r7
	mov	t27, r8
	mov	t28, r9
l2:
	add	t13, t0, t1
	add	t14, t13, t2
	add	t15, t14, t3
	add	t16, t15, t4
	add	t17, t16, t5
	add	t18, t17, t6
	add	t19, t18, t7
	add	t20, t19, t8
	add	t21, t20, t9
	mov	r0, t21
l3:
	mov	r10, t22
	mov	r4, t23
	mov	r5, t24
	mov	r6, t25
	mov	r7, t26
	mov	r8, t27
	mov	r9, t28
# Deallocate frame
	pop	{fp, pc}

.ltorg

.data
l1:
	.word 1
	.asciz "\n"

# Routine: _main
.global tc_main
.text
tc_main:
# Allocate frame
	mov	t40, r10
	mov	t41, r4
	mov	t42, r5
	mov	t43, r6
	mov	t44, r7
	mov	t45, r8
	mov	t46, r9
l4:
	mov	r1, fp
	ldr	t29, =0
	mov	r2, t29
	ldr	t30, =1
	mov	r3, t30
	ldr	t31, =2
	str	t31, [sp, #0]
	ldr	t32, =3
	str	t32, [sp, #4]
	ldr	t33, =4
	str	t33, [sp, #8]
	ldr	t34, =5
	str	t34, [sp, #12]
	ldr	t35, =6
	str	t35, [sp, #16]
	ldr	t36, =7
	str	t36, [sp, #20]
	ldr	t37, =8
	str	t37, [sp, #24]
	ldr	t38, =9
	str	t38, [sp, #28]
	bl	tc_l0
	mov	t12, r0
	mov	r1, t12
	bl	tc_print_int
	ldr	t39, =l1
	mov	r1, t39
	bl	tc_print
l5:
	mov	r10, t40
	mov	r4, t41
	mov	r5, t42
	mov	r6, t43
	mov	r7, t44
	mov	r8, t45
	mov	r9, t46
# Deallocate frame
	pop	{fp, pc}

.ltorg
$ echo $?
0

The runtime must be functional. No difference must be observable in comparison with a run with HAVM.

substring-0-1-1-arm.tig
substring("", 1, 1)
tc -e --target-arm --inst-display substring-0-1-1-arm.tig
$ tc -e --target-arm --inst-display substring-0-1-1-arm.tig
# Tiger final assembler ouput.

.data
l0:
	.word 0
	.asciz ""

# Routine: _main
.global tc_main
.text
tc_main:
# Allocate frame
	mov	t4, r10
	mov	t5, r4
	mov	t6, r5
	mov	t7, r6
	mov	t8, r7
	mov	t9, r8
	mov	t10, r9
l1:
	ldr	t1, =l0
	mov	r1, t1
	ldr	t2, =1
	mov	r2, t2
	ldr	t3, =1
	mov	r3, t3
	bl	tc_substring
l2:
	mov	r10, t4
	mov	r4, t5
	mov	r5, t6
	mov	r6, t7
	mov	r7, t8
	mov	r8, t9
	mov	r9, t10
# Deallocate frame
	pop	{fp, pc}

.ltorg
$ echo $?
0
tc -e --target-arm --asm-compute --inst-display substring-0-1-1-arm.tig
$ tc -e --target-arm --asm-compute --inst-display substring-0-1-1-arm.tig
# Tiger final assembler ouput.

.data
l0:
	.word 0
	.asciz ""

# Routine: _main
.global tc_main
.text
tc_main:
	push	{fp, lr}
	sub	fp, sp, #4
	sub	sp, sp, #0
l1:
	ldr	r1, =l0
	ldr	r2, =1
	ldr	r3, =1
	bl	tc_substring
l2:
	add	sp, sp, #0
	pop	{fp, pc}

.ltorg
$ echo $?
0
tc -e --target-arm --asm-display substring-0-1-1-arm.tig > substring-0-1-1-arm.s
$ tc -e --target-arm --asm-display substring-0-1-1-arm.tig > substring-0-1-1-arm.s

$ echo $?
0
arm-linux-gnueabihf-gcc -march=armv7-a -osubstring-0-1-1-arm substring-0-1-1-arm.s
$ arm-linux-gnueabihf-gcc -march=armv7-a -osubstring-0-1-1-arm substring-0-1-1-arm.s
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/bin/ld: warning: /tmp/ccrmuE1L.o: missing .note.GNU-stack section implies executable stack
/usr/lib/gcc-cross/arm-linux-gnueabihf/11/../../../../arm-linux-gnueabihf/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker
$ echo $?
0
qemu-arm -L /usr/arm-linux-gnueabihf ./substring-0-1-1-arm
$ qemu-arm -L /usr/arm-linux-gnueabihf ./substring-0-1-1-arm
substring: arguments out of bounds
$ echo $?
120

The following example illustrates conditional jumps.

condjump-arm.tig
if 42 > 51 then "forty-two" else "fifty-one"
tc -e --target-arm --inst-display condjump-arm.tig
$ tc -e --target-arm --inst-display condjump-arm.tig
# Tiger final assembler ouput.

.data
l0:
	.word 9
	.asciz "forty-two"

.data
l1:
	.word 9
	.asciz "fifty-one"

# Routine: _main
.global tc_main
.text
tc_main:
# Allocate frame
	mov	t4, r10
	mov	t5, r4
	mov	t6, r5
	mov	t7, r6
	mov	t8, r7
	mov	t9, r8
	mov	t10, r9
l5:
	ldr	t1, =42
	cmp	t1, #51
	bgt	l2
l3:
	ldr	t2, =l1
l4:
	b	l6 
l2:
	ldr	t3, =l0
	b	l4 
l6:
	mov	r10, t4
	mov	r4, t5
	mov	r5, t6
	mov	r6, t7
	mov	r7, t8
	mov	r8, t9
	mov	r9, t10
# Deallocate frame
	pop	{fp, pc}

.ltorg
$ echo $?
0
tc -e --target-arm --asm-compute --inst-display condjump-arm.tig
$ tc -e --target-arm --asm-compute --inst-display condjump-arm.tig
# Tiger final assembler ouput.

.data
l0:
	.word 9
	.asciz "forty-two"

.data
l1:
	.word 9
	.asciz "fifty-one"

# Routine: _main
.global tc_main
.text
tc_main:
	push	{fp, lr}
	sub	fp, sp, #4
	sub	sp, sp, #0
l5:
	ldr	r1, =42
	cmp	r1, #51
	bgt	l2
l3:
	ldr	r1, =l1
l4:
	b	l6 
l2:
	ldr	r1, =l0
	b	l4 
l6:
	add	sp, sp, #0
	pop	{fp, pc}

.ltorg
$ echo $?
0