Assignment 6: Parameter Passing

Overview

The goal of this assignment is to become familiar with a number of standard parameter-passing conventions.

Exploratory assignments

This week’s assignment is exploratory. We are asking you to use what you’ve learned in class—along with your knowledge from prior classes—to investigate an idea.

An exploratory assignment might require a different approach from the approaches you have used on previous assignments. There’s no one perfect way to do the assignment. Because you are writing code to learn about a topic, you might have to go through a few revisions of your work as your learning grows. A good approach is to be eager to talk things through with your partner or the course staff, to try things out, and to craft experiments that will tell you when you are or are not on the right path.

Assignment materials

This week, you must compile and run the coding part on Knuth.

Materials

Use the assignment workflow and the link provided on Piazza to get access to this week’s files.

Part 1: Read and take notes about calling conventions

Read the article on calling conventions in compiled languages. You’ll need to understand the similarities and differences among these calling conventions, so taking some notes is probably a good idea!

Part 2: Discover your calling (convention) [50%]

Can a program “know” something about its programming language? For example, imagine that you are a plain and simple function. You’re dropped into the world, with no idea how the world works. Is your programming language call-by-value? Call-by-macro? How could you find out?

In this part of the assignment, you will write a program that can discover what kind of calling convention is used by that program’s language!

Your task: In checkConvention.c, write a function callingConvention that discovers and describes which calling convention is being used by the programming language.

The checkConvention program is written using C-language syntax (which is also C++ syntax). By default, C and C++ use call-by-value. But we have a way to run the program using a different calling convention…

Running the program with different calling conventions

There is a compiler installed on knuth that will compile a program in a way that makes it appear to use another calling convention.

The best way to compile and execute checkConvention.c with another calling convention is to run this command:

    ./checkOne ByXXXX

where ByXXXX is one of ByValue, ByReference, ByResult, ByValueResult, ByName, ByNeed, ByMacro, BySadMacro, ByBrokenness.

This is also how you test your code: the output of the program should correspond to the calling convention used to run the program.

You can also run

    ./checkAll

which tries all the conventions.

Instructions and suggestions for implementing the program

  • Your callingConvention function must return a string that describes the language’s calling convention. For example, return the string "ByValue" if the language uses call-by-value parameter passing, return "ByReference" if the language uses call-by-reference, etc. There are no restrictions on the format of the string, so long as the string makes clear which calling convention has been identified.

  • You may (and probably should) use helper functions, global variables, etc.

  • Your code must
    • Contain only function and global variable definitions.

    • Pass only int values as arguments to your helper functions.

    • When passing an argument to a function, we must use simple expressions. Things like variables and the ++ operator should work, and can produce the effects that will be useful for testing. Things like function calls and array indices will not work.

    • Use no types other than int, bool, and string (which should be used for constant strings only).

    • Use no library functions (except rand(), which returns a random int). While you are debugging, you can use printf, which you can call using using normal call-by-value, e.g., printf("the value of i is %d\n", i). Be sure to remove any printf calls from your final code.

    • You may make the following simplifying assumptions:

      • When the convention is call-by-result, integer arguments will begin “uninitialized” either with some arbitrarily chosen constant, or have a truly random value.1
      • Call by reference, value-result, and result normally need actual arguments to be “l-values” (something like a variable that could appear on the left-hand-side of an assignment, i.e., something you could assign to), so that they can be changed by the call. You may assume that when these conventions are active, in a call like f(2, z, g(x)) the values of the arguments 2 and g(x) are copied into temporary memory locations before the call occurs (this is how FORTRAN 77 worked, and how C++ works when passing by-constant-reference); of course, any changes to these locations will be lost once the call completes. Thus, you are always allowed write to the parameter variable, it’s just that in some cases the written value will be lost once the function returns.
    • As a bonus, we can run the program with two versions of call-by-macro (ByMacro and BySadMacro, the latter is prone to more issues than the former), and with call-by-brokenness in which nothing is passed in or out at all. At least one of ByMacro and BySadMacro should cause your callingConvention function to return "by macro". Detecting "by brokenness" is not required, but you may want to take on the challenge of doing so.

Thinking and writing about calling conventions

For this part, put your answers in the file Answers.txt. Be sure to make your meaning clear and unambiguous.

Part A [40%]

Assume we have the code

    void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    }

Depending on the arguments and the calling convention, applying swap to arguments may or may not have the intended effect.

For this problem, we are concerned with the first six calling conventions, but not call by need.

  1. Assume swap is applied to two integer variables, x and y. For four of the six calling conventions, swap(x,y) will successfully make x and y exchange contents. For each of the six calling conventions, explain why the call to swap would or would not work.

  2. The previous part told you that the swapped variables were two distinct variables with the specific names x and y. One of the four parameter-passing techniques from the previous part may work in that situation but fail when you try to swap two other variables. Which technique, which other variables, and why?

  3. After the previous part, three of the calling conventions work for swap. Now, assume we have an integer variable i and an array of integers a. Assuming that a has a length of at least i+1, two of these three conventions would make swap(i,a[i]) work as expected. For all three conventions, explain why this call to swap would or would not work.

Part B [10%]

In your own words, carefully explain the error(s) in the following argument:

Java passes simple types like integers by value (because integer values get copied) but passes object types by reference (because when an object is passed, implicitly a pointer is being copied, and call by reference is the convention that is implemented by passing implicit pointers).

  1. To up the challenge, in our system, the “arbitrarily chosen constant” is chosen somewhat adversarially. For example, if you try passing 42, on some runs it may decide that 42 is the arbitrary constant it always initializes the variable to; if you’re not careful, this could fool your code into thinking the parameter was successfully passed to the function. But within a run of the ByResult tester, the “arbitrary constant” will be the same for every function call. One way to handle this is to use a probabilistic strategy that could give a wrong answer once or twice, but is overwhelmingly unlikely to do so repeatedly in practice.