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
, andstring
(which should be used for constant strings only). -
Use no library functions (except
rand()
, which returns a randomint
). While you are debugging, you can useprintf
, 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 anyprintf
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 arguments2
andg(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
andBySadMacro
, 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 ofByMacro
andBySadMacro
should cause yourcallingConvention
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.
-
Assume
swap
is applied to two integer variables,x
andy
. For four of the six calling conventions,swap(x,y)
will successfully makex
andy
exchange contents. For each of the six calling conventions, explain why the call toswap
would or would not work. -
The previous part told you that the swapped variables were two distinct variables with the specific names
x
andy
. 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? -
After the previous part, three of the calling conventions work for
swap
. Now, assume we have an integer variablei
and an array of integersa
. Assuming thata
has a length of at leasti+1
, two of these three conventions would makeswap(i,a[i])
work as expected. For all three conventions, explain why this call toswap
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).
-
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 that42
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 theByResult
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. ↩