Session 18
Toward a Run-Time System in TM

Set Up

Next time, we begin our study of the run-time system that enables a compiled program to run. Your project targets a virtual machine, TM. Both the compiled program and the run-time system will run in TM.

Today, we do a sequence of exercises leading from the simplest TM program up to a TM program that calls two functions in the simplest fashion possible. The goals are to help you begin to familiarize yourself with the TM assembly language and to demystify what, for many, is the most complex part of code generation: calling functions. For each exercise, I give a solution to the exercise, along with some slides I used while discussing the code.

Note: I do not provide a full set of expository notes for today's session. Instead, I provide all the materials I use and display in class, including working TM programs. Here are the main slides for the day, which step through the exercises. You can use them in parallel with the exercises that follow.

To prepare for today, you worked through a lab exercise in which you downloaded, compiled, and ran two versions of the TM simulator. You may want to put those TM simulators in your path... You will be using them a lot the rest of the semester.

Quick Introduction to TM

Use this TM instruction set handout to review the instructions available to us in our programs.

For today, you read a little bit about the TM simulator but perhaps not about TM itself. It has a small instruction set with:

Exercise 1

Write a TM program that prints 1.

Solution:

Discussion

Slide 1: Lines are numbered in TM programs.

Slide 2: The order of the lines in the program file does not matter.

Slides 3-5: This program uses three instruction codes.

Note: All TM registers are initialized to 0.

Slide 6: This TM program is a simplified version of the shortest possible Klein program, which we discussed in Session 13:

function main(): integer
   1

But the Klein spec says:

Every program must define a function named main, which is called first when the program runs.

We'll work our way up to doing this.

Exercise 2

Write a TM program that computes 3 + 4 and prints the result.

Solution:

Discussion

Slide 1: TM supports arithmetic only in registers.

Slide 2: This corresponds to a near-minimal Klein program:

function main(): integer
   3 + 4

Exercise 3

Write a TM program that squares its command-line argument and prints the result.

Solution:

Before Starting

Where will our TM program find the command-line arguments?

The original version of TM did not have command-line arguments, only user input. Unfortunately for us, Klein does not have user input, only command-line arguments.

A home-grown extension to TM places command-line arguments in DMEM[1..n].

When the TM virtual machine starts, DMEM[0] holds the number of the last slot of DMEM, by default 1023. The rest of the slots hold 0.

Note: IMEM is distinct from DMEM.

Discussion

Slide 1: LD computes the address ([rX] + offset) and reads DMEM.

Even code this short may benefit from inline comments.

Slide 2: In this particular example, I use r0 to compute. From now on, though, I will use r0 as 0 — a TM idiom!

Slide 3: A program with two arguments doesn't look much different.

Slide 4: It benefits from using the R0 idiom.

Show the TM stepper, especially:

s[tep] n   —  Execute n (default 1) TM instructions
r[egs]     —  Print the contents of the registers
d[Mem] b n —  Print n dMem locations starting at b

Slide 5: The two-argument version corresponds to a Klein program with arguments:

function main(m: integer, n: integer): integer
   3 + 4

But — again. What about calling main as a function?

Exercise 4

Same as #3 (square the argument and print the result) but by jumping to the multiplication instruction.

Solution:

Before Starting

Exercise Slide 5: What does it mean to "jump to an instruction"? The program transfers control to another location in the code, where it operates on some values, produces an answer, and branches back. The arguments coming in to the function, the value going out, and the place to branch back to must all be placed in locations that both the branching code and the computing code know about.

For now, these can be registers.

Exercise Slide 6: This idea led to one of the first design patterns in the history of programming: Subroutine. This pattern is implemented as a language primitive in nearly every programming language we use, so we rarely think of it as a pattern at all.

Exercise Slides 7-8: To write your Klein compiler, though, you will need to implement the pattern in TM assembly language.

Discussion

Slide 1: Code this long benefits from inline comments.

This program uses registers in place of slots stored in a stack frame. When we do use stack frames, they will be in DMEM.

In the code, note:

  • Lines 2 and 7: the use R7 as the program counter
  • Line 6: the use of ADD with R0 to move a value to a new register

Yes, we could have MULtiplied into R4 directly. In general, though, the program will have to move its result to the result slot on the stack.

Slides 2 and 3: Highlight the calling sequence in the calling function and the called function.

Slides 4 and 5: Highlight the return sequence in the called function and the calling function.

Slide 6: This code corresponds to a Klein program with a single argument.

function main(n: integer): integer
   n * n

Slide 7: What if square(n) is a function, too?

function main(n: integer): integer
   square(n)

function square(m: integer): integer
   m * m

Exercise 5

Same as #4 (square the argument and print the result) but by calling both main(n) and square(m).

Solution:

Discussion

We do not always get to the final exercise in class. If we did not get there, try it in the comfort of your own home, and only then look at the code and slides.

We now need more registers to store arguments, return values, and return addresses. We still have room(!), but this program shows how to use DMEM to save R6 in MAIN before calling SQUARE and how to return to that location at the end of MAIN.

Consider the calling and return sequences:

  • calling sequence, run-time to main (Slides 2-3: caller and called)
  • calling sequence, main to square (Slides 4-5: caller and called)
  • return sequence, square to main (Slides 6-7: called and caller)
  • return sequence, main to run-time (Slides 8-9: called and caller)

Wrap Up

You can download all the materials for today: code, slides, and notes, as a single archive.

If you have any questions, comments, or suggestions, please let me know.