You are on page 1of 32

MIPS Pointers,

Objects
CS270
Max Luttrell, Fall 2016
addresses
• every piece of data has an address

• in C, and MIPS assembly, the hexadecimal


address is generally hidden from us
generated code:
in C:
globalint: .space 4
int globalint;
...
main() {
li $t0,6
globalint = 6;
sw $t0, globalint
}
references - C++
• in C++, we can pass parameters to functions by
reference. again, we don't see the hexadecimal
address

void fillt(int &x) {


x=2;
}

main() {
int y;
fillt(y);
}
pointers - C
• in C, we can not pass a parameter to a function
by reference

• we can, however, pass a pointer, which is an


address
void fillt(int *x) {
*x=2;
}
main() {
int y;
fillt(&y);
}
pointers - MIPS
• in MIPS, we have used pointers to access
arrays.

# A[4] = 0;
la $t0, A # load base address of A to $t0
sw $zero, 16($t0) # offset of A[4] is 16

-or-

# A[4] = 0;
la $t0, A
addi $t0, $t0, 16
sw $zero, 0($t0)
arrays vs pointers
• let's say we want to clear an array with at least one
element to all zeros

• here's two solutions in C++:


// assume size>0
void clear1(int array[], int size) {
int i;
for (i = 0; i < size; i++)
array[i] = 0;
}

// assume size>0
void clear2(int *array, int size) {
int *p;
for (p = &array[0]; p < &array[size]; p++)
*p = 0;
}
clear1
// assume size>0
void clear1(int array[], int size) {
int i;
for (i = 0; i < size; i++)
array[i] = 0;
}

# use $t0 for i


move $t0, $zero # i = 0
loop:
sll $t1, $t0, 2 # $t1 = i*4
add $t2, $a0, $t1 # $t2 = address of array[i]
sw $zero, 0($t2) # array[i] = 0
addi $t0, $t0, 1 # i++
blt $t0, $a1, loop # if (i<size) goto loop
clear2
// assume size>0
void clear2(int *array, int size) {
int *p;
for (p = &array[0]; p < &array[size]; p++)
*p = 0;
}

# use $t0 for p


move $t0, $a0
loop2:
sw $zero, 0($t0) # *p = 0
addi $t0, $t0, 4 # p++ (point p to next word)
sll $t1, $a1, 2 # $t1 = size*4
add $t2, $a0, $t1 # $t2 = address of array[size]
blt $t0, $t2, loop2 # if (p<&array[size]) goto loop2
clear2
• more efficiently we could compute address of
array[size] before the loop

# use $t0 for p


move $t0, $a0
# compute address of array[size] before loop
sll $t1, $a1, 2 # $t1 = size*4
add $t2, $a0, $t1 # $t2 = address of array[size]
loop2:
sw $zero, 0($t0) # *p = 0
addi $t0, $t0, 4 # p++ (point p to next word)
blt $t0, $t2, loop2 # if (p<&array[size]) goto loop2
clear1 vs. clear2
# clear1
# use $t0 for i
move $t0, $zero # i = 0
loop:
sll $t1, $t0, 2 # $t1 = i*4
add $t2, $a0, $t1 # $t2 = address of array[i]
sw $zero, 0($t2) # array[i] = 0
addi $t0, $t0, 1 # i++
blt $t0, $a1, loop # if (i<size) goto loop

# clear2
# use $t0 for p
move $t0, $a0
# compute address of array[size] before loop
sll $t1, $a1, 2 # $t1 = size*4
add $t2, $a0, $t1 # $t2 = address of array[size]
loop2:
sw $zero, 0($t0) # *p = 0
addi $t0, $t0, 4 # p++ (point p to next word)
blt $t0, $t2, loop2 # if (p<&array[size]) goto loop2
review: jal, jr
• we have been using the jal instruction to jump-
and-link. this jumps to a location and also stores
the return point in register $ra. this example
jumps and links to function strdup:

jal strdup

• we have also used the jr instruction to jump to an


address stored in a register. this example jumps
to the location stored in $ra:

jr $ra
jalr
• MIPS provides another jump instruction, jalr, which
combines these two features. it jumps to the
address stored in a register, and also stores the
return address in $ra.

# load address of strdup function


# to $t0
la $t0, strdup

# jump to address stored in $t0


# (strdup) and also save return
# point in $ra
jalr $t0
function pointer
• we can create a static data word to hold the
address of a function, a.k.a. a function pointer

.data
funcptr: .word strdup

...
.text
#then at the call
lw $t0, funcptr
jalr $t0
function pointer
• we could also create the function pointer at run time:

.data
funcptr: .word 0

...
.text
la $t0, strdup
sw $t0, funcptr

#then at the call


lw $t0, funcptr
jalr $t0
function pointer in C: qsort
• a well-known use of a function pointer is qsort in
the C standard library. you must pass in a
comparison function as a parameter
#include <cstdlib>
#include <iostream>
using namespace std;

#define NELEMS 10
int ia[NELEMS] = {4, -5, 6, 10, 25, -43, 22, 9, -2, 10};

int compare_ints( int * first, int * second) {


if (*first < *second) return -1;
return( *first > *second);
}

int main() {
int i;
qsort(ia,NELEMS,sizeof(int),compare_ints);
for (i=0;i<NELEMS;i++) cout << ia[i] << endl;
}
To compile on hills: g++ -fpermissive qsort.cpp
object-oriented
programming
• most modern high-level languages use object-
oriented programming

• advantage: abstract out details of


implementation

• at assembly level, objects don't exist


object-oriented
programming review
• an object is an instance of a class that contains
members. a member can be either:

• data (a.k.a. properties)

• functions (a.k.a. methods)


object-oriented
programming: data
• data members

• data members of class are placed together in


a compound data structure similar to a C
struct. the struct size is the sum of the sizes of
all the members

• each member is given an offset from the start


of the object
object-oriented
programming: data
• for example, given this class and declaration of
one instance of it:
class xyz {
int a,b;
public:
int add(void) {return (a+b);}
xyz (int mya, int myb) { a = mya; b = myb; }
int sum(void) { return(a+b); }
int sum(int i){ return(a+b+i); }
};

xyz myxyz(2,3);
• the size of myxyz is 8 bytes (two 4-byte ints)

• the address of member a is the same as address of myxyz

• the address of member b is 4+address of myxyz


object-oriented
programming: functions
• member functions look like they appear in the
class, but in fact are not stored with the data

• each function is at a constant address with a


label, like any MIPS function

class xyz {
int a,b;
public:
int add(void) {return (a+b);}
xyz (int mya, int myb) { a = mya; b = myb; }
int sum(void) { return(a+b); }
int sum(int i){ return(a+b+i); }
};
object-oriented
programming: functions
• how can we determine the label to call a member
function? there are two issues:

• another class could have a add() function

• member functions can be overloaded, like our sum()


function

class xyz {
int a,b;
public:
int add(void) {return (a+b);}
xyz (int mya, int myb) { a = mya; b=myb; }
int sum(void) { return(a+b); }
int sum(int i){ return(a+b+i); }
};
name mangling
• we use name mangling -- a standard format to
create a unique label for each member function

• encodes the class, function name, and


argument types into a unique label

• also, the label generated must not be legal to


use by the C++ programmer
name mangling

simple example
one possible name mangling scheme:

• class name

• $ (illegal character for user)

• function name

• $ (illegal character for user)

• abbreviation of argument types


class xyz {
int a,b;
public:
int add(void) {return (a+b);} // xyz$add$v
xyz (int mya, int myb) { a = mya; b=myb; } // xyz$$ii
int sum(void) { return(a+b); } // xyz$sum$v
int sum(int i){ return(a+b+i); } // xyz$sum$i
};
calling a member function
• if we want to call myxyz.add(4), we need to
know where myxyz's data is

• address of the object being operated on is


passed as the first argument

• myxyz.sum(4) would do a jump and link to


xyz$sum$i with two arguments: &myxyz and 4
class xyz {
int a,b;
public:
int add(void) {return (a+b);} // xyz$add$v
xyz (int mya, int myb) { a = mya; b=myb; } // xyz$$ii
int sum(void) { return(a+b); } // xyz$sum$v
int sum(int i){ return(a+b+i); } // xyz$sum$i
};
MIPS class implementation
#class xyz {
# int a,b;
#public:
# int sum(void) {return (a+b);}
# int sum(int i) {return(a+b+i);}
# xyz (int mya, int myb) { a = mya; b=myb; }
#};
.globl xyz$sum$v
xyz$sum$v:
lw $t0,0($a0) # a
lw $t1,4($a0) # b
add $v0,$t0,$t1
jr $ra
.globl xyz$sum$i

xyz$sum$i:
lw $t0,0($a0) # a
lw $t1,4($a0) # b
add $v0,$t0,$t1
add $v0,$v0,$a1
jr $ra
...
MIPS class implementation

#class xyz {
# int a,b;
#public:
# int sum(void) {return (a+b);}
# int sum(int i) {return(a+b+i);}
# xyz (int mya, int myb) { a = mya; b=myb; }
#};
...
# constructor
.globl xyz$$ii
xyz$$ii:
sw $a1,0($a0)
sw $a2,4($a0)
jr $ra
MIPS class usage
#class xyz {
# int a,b;
#public:
# int sum(void) {return (a+b);}
# int sum(int i) {return(a+b+i);}
# xyz (int mya, int myb) { a = mya; b = myb; }
#};

# void PrintInteger(int i) --- part of util.s

#int main() {
# int result; // kept in $v0
# xyz myxyz (2,4);
# result = myxyz.sum();
# PrintInteger(result);
# result = myxyz.sum(1);
# PrintInteger(result);
#}
#
MIPS class usage
#int main() {
.globl main
main:
# need std stack frame, plus one s-reg (for address
# of myxyz) plus 8 bytes for myxyz
addiu $sp,$sp,-32
sw $ra,28($sp)
sw $s0,24($sp)
# myxyz is at 16($sp)
la $s0,16($sp)

# int result; // kept in $v0


# xyz myxyz (2,4);
move $a0,$s0
li $a1,2
li $a2,4
jal xyz$$ii
...
MIPS class usage
...
# result = myxyz.sum();
move $a0,$s0
jal xyz$sum$v
# PrintInteger(result);
move $a0,$v0
jal PrintInteger

# result = myxyz.sum(1);
move $a0,$s0
li $a1,1
jal xyz$sum$i
# PrintInteger(result);
move $a0,$v0
jal PrintInteger
#}
lw $s0,24($sp)
lw $ra,28($sp)
addiu $sp,$sp,32
jr $ra

#
.include "/pub/cs/mluttrel/cs270/util.s"
Exercise 6A
• Take the strdup.s program that you wrote in Exercise 5D
and modify the strdup function so that when it copies the
string to the newly-allocated buffer, it reverses it. You
should also change the name of the function and the
program to revstring.

• If your version of strdup.s used strcpy(), you will have to


change this part to copy the bytes in a loop in reverse
order instead of using strcpy()

• This is a good exercise for pointers, as revstring()


requires you to use pointers going in opposite directions.

• If you don't have a solution to strdup handy, my solution


is here:

/pub/cs/mluttrel/cs270/exercises/procedures/answers
Exercise 6B - fpointer
1. Create MIPS code for two simple leaf functions:
int adder(int x, int y) { return x+y; }
int subber(int x, int y) { return x-y; }

2. Now, create a MIPS function which has three arguments:

int doit(int x, int y, fp);

• The third argument fp is a function pointer that is used on the first two
arguments. It is used to call a function to apply to the first two arguments
(either adder or subber).

• In your doit function, put the third argument $a2 into $t0. Then use the
instruction jalr $t0 to call the function $a2 points to.

• Note: doit is not a leaf function, and you need to follow the procedure
calling convention, i.e. allocate a stack frame and save/restore $ra.

3. Finally, write a simple main function which loads two ints into $a0 and $a1,
and then the address of the function you wish to call in $a2 (try adder first), and
then calls doit. Print out the result. Then try another call to doit with the the
address of subber. Don't forget the procedure calling convention for main().
Exercise 6C
Begin Assignment 6, String Builder

You might also like