References
Programming with the goto
- There is no while in machine language
- Programming education in the old days
- Linus Torvalds on the
goto
Stuff to do
Start by downloading a C project into your csci235 directory.
In your solution, do not modify collatz.c. Only change pa-collatz.c. That way you will always have a copy of a standard C program.
Pointers — How it is done
Using the “facts about arrays” in the
C pointers page, transform
the two array element assignments in the
fill_collatz_table
so that that neither assignment uses
the brackets. We’re trying for the most
machine-like implementation. This means using
void *
in the second assignment.
Gotos
Now we are heading back to programming in the 60’s.
Open the C programming with the goto
handout in another window.
The worse if
Yes, the goto
has been banned in Java and almost all
computer science courses.
However work your way down to the discussion of the
if
and the
if
—else
.
First, apply this transformation to the
if
in the fill_collatz_table
routine.
This is probably your first label and your first goto
.
Be sure to compile and run your program before proceeding.
Now apply the rule for translating the
if
—else
to collatz
.
Imagine the spaghetti
code we used to write in our introductory programming courses!
When the if
’s are nested, the spaghetti gets
very tangled.
The for
Let’s replace that for
in fill_collatz_table
with a while
.
You’ll find this rule about 70% of the way through
the handout.
Sometimes this transformation is mentioned in the first programming course.
Again, compile and run before plunging on.
The while
This is the hard one. Once you add the backward branching goto
,
it gets particular messy.
Go to the discussion of the while
.
You will notice two transformation rules.
I suggest you use the second as it tends to be a little faster
and, of course, more complex!
You’ll find this rule about 60% of the way through
the handout.
First apply the rule to the while
in the
collatz
. Do not think too hard!
Just apply the rule.
Test before proceeding.
And finally apply the rule to the remaining while
,
which used to be a for
,
in fill_collatz_table
.
What about Java?
Download a tar-gzip of the collatz program written in Java. Extract it and then compile and run it with the following commands. (Isn’t the inconsistency odd.)
javac edu/unca/csci235/Collatz.java java edu.unca.csci235.Collatz
Run a couple of commands using javap, the Java disassembler, to view and save the compiled Java bytecode.
javap edu.unca.csci235.Collatz javap -c edu.unca.csci235.Collatz | more javap -c edu.unca.csci235.Collatz > Collatz.java-bytecode
Take a look at A Java Programmer’s Guide to Byte Code.
The Java Virtual Machine is a stack-based computer that is simulated in
software.
Open up your Collatz.java-bytecode
and go to the collatz
routine. (It will be around address 262.)
Add some ”comments“ to your code describing what is happen.
It should start with something like this:
static int collatz(int); Code: 0: iconst_0 [ 0 ] 1: istore_1 [ ] Store in count 2: iload_0 [ n ] 3: iconst_1 [ n 1 ] 4: if_icmpeq 33 [ ] Jumps if n == 1
Three significant instruction sets
RISC
Modern Reduced Instruction Set Computers have fairly simple instruction sets. There are load/store instructions that can move the memory location to/from a register. Generally these instructions index memory relative to a register, which can be a stack or frame pointer, a global offset (this one is hard), or a register containing a C pointer.
An assignment statement, such as x = y + z
requires, in the worse case, four instructions.
y
, is loaded into one register.z
, is loaded into another register.- The two registers are added.
- The result is stored in
x
.
Storing variables in registers, rather than memory, greatly improve performance.
An integer array index, such as V[i]
requires,
in the worse case, five instructions.
- The array pointer,
V
, is loaded into a register. - The array index,
i
, is loaded into a register. - The array offset,
i*4
is computed by multiplying the array index by four. This is done with a left shift:i<<2
. - The array offset is added to
the array pointer to obtain a reference to the element location,
&A[i]
, that is&A*i
. - A load instruction is used to obtain
A[i]
.
Of course, most compilers do much better by keeping
&A[i]
in a register.
Branches are performed with the control flow instructions listed
in Sample PIC32 (MIPS) instructions
Finally, note that the ABI (application binary interface) specifies
how registers are used. Note that
$a0
to $a3
are used to pass arguments and
$v0
and $v1
are used to return results.
IBM System 360
Look at a copy of the
IBM System/360 Green card stored at the
Computer History Museum.
Notice that the LD
instruction could add two registers
and a displacement to obtain an address.
However, the displacement is only twelve bits long.
The Intel x86-64 instruction set
The Intel x86-64 is the the ultimate
CISC, Complex Instruction Set Computer.
Take a look at the
x86-64 instruction set.
Go on to a summary of the
many options
for the ADD
instruction.
We will return to this in Chapter 3. We will return to this in CSCI 335.
The assigned task
Take a look at the file pseudo_regs.h . It defines several fake registers that we will use to simulate an assembly language program.
_it0
to_it3
— temporary registers_ia0
to_ia3
— argument registers_ir0
to_ir1
— return registers
Hopefully, you have a copy of the pc-collatz.c program we wrote last week. If you don’t, you can download this one which contains low-level control structures.
By the way, this is the first time I have tried this lab. It might be rough!
Subtask One
Replace count
with the return ”register“
_ir0
.
Subtask Two
Replace n
with the argument ”register“
_ia0
.
Subtask Three
This one is tricky.
In the collatz
function,
replace complex statements like
_ia0 = 3 * _ia0 + 1 ;
with sequences of statements
where the right-hand side of an assignment contains
no more than
one arithmetic operations and the operands to arithmetic
operations are registers.
This means that the assignment seen above must be replaced with
three assignments.
You will need to use a temporary register
_it0
to do this.
Subtask Four
This one is messy.
Start by changing the header of
collatz
to the following
void collatz(void)
Now modify fill_collatz_table
to pass its
argument in _ia0
and receive its result
in _ir0
.
This is the real world.
Subtask Five
This one is huge because it repeats all the previous subtasks, but
for fill_collatz_table
.
Modify fill_collatz_table
so that it receives its arguments in
_pa0
and
_ia1
.
Notice, that it must be _pa0
rather than
_ia0
to make the C compiler happy!
If you look at a pseudo_regs.h, you
will see how the magic of the union
makes this possible.
Also, replace i
with a temporary register.
You will also need to modify print_table
to pass arguments
to fill_collatz_table
in the appropriate registers.
However, the really tricky thing is that fill_collatz_table
will need to save copies of its argument and and temporary registers
before calling collatz
. You will do this with code
sequences
similar to the following before the call:
saveTemp0 = _it0 ;
and similar to the following after the call:
_it0 = saveTemp0 ;
Such is the life of an
assembly language programmer.
There will also be some very messy C to handle the array accesses. Hint:
_it1 = _it0 << 2 ; _pt1 = (void *)(((unsigned long)(void *)_pa0) + _it1) ; *((int *)_pt1) = _ir0 ;