Test Codes for Call, Tail-Call, and Return Instructions

Introduction

This file contains assembly code and object code to help you test your call, tailcall, and return instructions. Because everybody’s assembly language and object code is different, you likely can’t use them directly. Instead,

Once you get the right .vo files and all three of your instructions are working, you should be able to produce evidence like this:

> svm *.vo
The only test passed.
The only test passed.
The only test passed.
The only test passed.
The only test passed.
The only test passed.
The only test passed.
> svm *.voerr
This test should cause a checked run-time error
Run-time error (no stack trace): Callee expects 0 parameters but got 1

You can download all the tests in file tests07.zip.

Tests for the call instruction

For the call instruction, I provide two passing tests and one failing test:

  1. See if control is transferred from the caller to the callee. The callee runs check and expect, then halts, so this test doesn’t need a return.

    r0 := function (0 arguments) {
      r1 := "arrived"
      check "arrived", r1
      expect "arrived", r1
      halt
    }
    r0 := call r0 ()

    If your assembler can’t parse that, here’s the .vo code:

    .load module 2
    .load 0 function 0 4
    loadliteral 1 string 7 97 114 114 105 118 101 100
    check 1 string 7 97 114 114 105 118 101 100
    expect 1 string 7 97 114 114 105 118 101 100
    halt
    call 0 0 0
  2. See if an argument can be passed and the register window works correctly. This code puts the function in register 200 and the argument in register 201. But for the test to pass, the callee must see the actual parameter in register 1. Again, the callee halts, so the test doesn’t need a return.

    r200 := function (1 arguments) {
      check "argument", r1
      r100 := "parameter test"
      expect "internal literal", r100
      halt
    }
    r201 := "parameter test"
    r0 := call r200 (r201)

    If your assembler can’t parse that, here’s the .vo code:

    .load module 3
    .load 200 function 1 4
    check 1 string 8 97 114 103 117 109 101 110 116
    loadliteral 100 string 14 112 97 114 97 109 101 116 101 114 32 116 101 115 116
    expect 100 string 16 105 110 116 101 114 110 97 108 32 108 105 116 101 114 97 108
    halt
    loadliteral 201 string 14 112 97 114 97 109 101 116 101 114 32 116 101 115 116
    call 0 200 201
  3. See if the SVM detects a function that is called with the wrong number of arguments. This test should trigger a checked run-time error.

    r200 := function (0 arguments) {
      r100 := "If you see this message, the test fails"
      println r100
      halt
    }
    r201 := "This test should cause a checked run-time error"
    println r201
    r0 := call r200 (r201)

    And the object code:

    .load module 4
    .load 200 function 0 3
    loadliteral 100 string 39 73 102 32 121 111 117 32 115 101 101 32 116 104 105 115 32 109 101 115 115 97 103 101 44 32 116 104 101 32 116 101 115 116 32 102 97 105 108 115
    println 100
    halt
    loadliteral 201 string 47 84 104 105 115 32 116 101 115 116 32 115 104 111 117 108 100 32 99 97 117 115 101 32 97 32 99 104 101 99 107 101 100 32 114 117 110 45 116 105 109 101 32 101 114 114 111 114
    println 201
    call 0 200 201

Tests for the return instruction

  1. See if control is transferred to a callee and back, and if the register window is restored correctly.

    goto L
    r0 := "This code should never have been executed"
    error r0
    error r0
    error r0
    L:
    r100 := function (0 arguments) {
      r0 := r0
      return r0
    }
    r1 := "test value"
    check "initial assignment to r1", r1
    r0 := call r100
    expect "final value of r1", r1

    And the object code:

    .load module 10
    goto 4
    loadliteral 0 string 41 84 104 105 115 32 99 111 100 101 32 115 104 111 117 108 100 32 110 101 118 101 114 32 104 97 118 101 32 98 101 101 110 32 101 120 101 99 117 116 101 100
    error 0
    error 0
    error 0
    .load 100 function 0 2
    move 0 0
    return 0
    loadliteral 1 string 10 116 101 115 116 32 118 97 108 117 101
    check 1 string 24 105 110 105 116 105 97 108 32 97 115 115 105 103 110 109 101 110 116 32 116 111 32 114 49
    call 0 100 100
    expect 1 string 17 102 105 110 97 108 32 118 97 108 117 101 32 111 102 32 114 49        
  2. See if a value can be returned correctly.

    r100 := function (0 arguments) {
      r1 := 1852
      return r1
    }
    r50 := call r100
    check "result from calling function", r50
    r200 := 1852
    expect "value we expected", r200

    And the object code:

    .load module 5
    .load 100 function 0 2
    loadliteral 1 1852
    return 1
    call 50 100 100
    check 50 string 28 114 101 115 117 108 116 32 102 114 111 109 32 99 97 108 108 105 110 103 32 102 117 110 99 116 105 111 110
    loadliteral 200 1852
    expect 200 string 17 118 97 108 117 101 32 119 101 32 101 120 112 101 99 116 101 100
  3. Test recursion. Here’s a factorial test:

    r0 := function (1 arguments) {
      ;; parameter n is in r1
      r2 := 0
      r2 := r1 = r2
      if r2 goto L1
        ;; induction step, n > 0
        r2 := r0  ;; the factorial fucntion
        r3 := 1
        r3 := r1 - r3
        r2 := call r2 (r3)
        r2 := r1 * r2
        return r2
      L1:
        ;; base case: n == 0
        r0 := 1
        return r0
    }
    global factorial := r0
    
    r1 := 5
    r0 := call r0 (r1)
    check "(factorial 5)", r0
    r0 := 120
    expect "120", r0

    And the object code:

    .load module 7
    .load 0 function 1 12
    loadliteral 2 0
    = 2 1 2
    if 2
    goto 6
    move 2 0
    loadliteral 3 1
    - 3 1 3
    call 2 2 3
    * 2 1 2
    return 2
    loadliteral 0 1
    return 0
    setglobal 0 string 9 102 97 99 116 111 114 105 97 108
    loadliteral 1 5
    call 0 0 1
    check 0 string 13 40 102 97 99 116 111 114 105 97 108 32 53 41
    loadliteral 0 120
    expect 0 string 3 49 50 48

Tests for the tailcall instruction

  1. Test a function that run half a million iterations, then stops. (My SVM completes this test in a tenth of a second.) This one doesn’t test that registers are moved correctly: the registers used in the caller and callee are exactly the same.

    ;; (define big (n) (if (= n 0) n (big (- n 1))))
    r0 := function (1 arguments) {
      ; parameter n is in r1
      r2 := 0
      r2 := r1 = r2
      if r2 goto L1
        r2 := 1
        r1 := r1 - r2
        tailcall r0 (r1)
      L1:
        return r1
    }
    global big := r0
    
    ;;  (check-expect (big 555000) 0)
    r1 := 555000
    r0 := call r0 (r1)
    check "(big 555000)", r0
    r100 := 0
    expect "0", r100

    Here’s the object code:

    .load module 7
    .load 0 function 1 8
    loadliteral 2 0
    = 2 1 2
    if 2
    goto 3
    loadliteral 2 1
    - 1 1 2
    tailcall 0 1
    return 1
    setglobal 0 string 3 98 105 103
    loadliteral 1 555000
    call 0 0 1
    check 0 string 12 40 98 105 103 32 53 53 53 48 48 48 41
    loadliteral 100 0
    expect 100 string 1 48
  2. Here’s a test that does move registers! The SVM must move all three actual parameters and r0. The test comes from vScheme code:

    (define times-plus (n m product) ; returns n * m + product
      (if (= n 0)
          product
          (times-plus (- n 1) m (+ m product))))
    
    (check-expect (times-plus 1200000 12 99) 14400099)

    Here’s my assembly code:

    r0 := function (3 arguments) {
      ;; function times-plus: r1 = n, r2 = m, r3 = product
      r4 := 0
      r4 := r1 = r4
      if r4 goto L1
        r4 := r0
        r5 := 1
        r5 := r1 - r5
        r6 := r2
        r7 := r2 + r3
        tailcall r4 (r5, ..., r7)
      L1:
        return r3
    }
    global times-plus := r0
    
    r1 := 1200000
    r2 := 12
    r3 := 99
    r0 := call r0 (r1, ..., r3)
    check "(times-plus 1200000 12 99)", r0
    r0 := 14400099
    expect "14400099", r0

    And my virtual object code:

    .load module 9
    .load 0 function 3 11
    loadliteral 4 0
    = 4 1 4
    if 4
    goto 6
    move 4 0
    loadliteral 5 1
    - 5 1 5
    move 6 2
    + 7 2 3
    tailcall 4 7
    return 3
    setglobal 0 string 10 116 105 109 101 115 45 112 108 117 115
    loadliteral 1 1200000
    loadliteral 2 12
    loadliteral 3 99
    call 0 0 3
    check 0 string 26 40 116 105 109 101 115 45 112 108 117 115 32 49 50 48 48 48 48 48 32 49 50 32 57 57 41
    loadliteral 0 14400099
    expect 0 string 8 49 52 52 48 48 48 57 57