Invoking a method on a object in Java
Polymorphic dispatch
xxxxxxxxxx
191class A {
2 void ping() {...}
3 void pong() {...}
4}
5class B extends A {
6 void ping() {...}
7 void pong() {...}
8 void wiff() {...}
9}
10
11static void foo(A a) {
12 a.ping();
13 a.pong();
14}
15
16static void bar() {
17 foo(new A());
18 foo(new B());
19}
Method address is determined dynamically
Class Jump table
Function pointer
A variable that stores a pointer to a procedure
Declared:
<return-type> (*<var-name>)(<argument-list>);
Dynamic call uses same format as regular function call:
<var-name> (<arguments>);
Example:
Use a struct to store jump table:
Class jump table declaration:
xxxxxxxxxx
41struct A_class {
2 void (*ping) (void *);
3 void (*pong) (void *);
4};
Instance methods:
xxxxxxxxxx
21void A_ping (void *this) {...}
2void A_pong (void *this) {...}
Static class object:
struct A_class A_class_obj = {A_ping, A_pong};
Object (instance of class)
xxxxxxxxxx
31struct A {
2 struct A_class *class;
3}
xxxxxxxxxx
51struct A *new_A() {
2 struct A *obj = malloc(sizeof(struct A));
3 obj->class = &A_class_obj;
4 return obj;
5}
Object use:
struct A *a = new_A();
a->class->ping(a);
a->class->pong(a);
Extending a Class
xxxxxxxxxx
51struct B_class {
2 void (*ping) (void *);
3 void (*pong) (void *); // or struct A_class super_class;
4 void (*wiff) (void *);
5}
xxxxxxxxxx
31void B_ping (void *this) {...}
2void B_wiff (void *this) {...}
3struct B_class B_class_obj = {B_ping, A_pong, B_wiff}
xxxxxxxxxx
81void foo (struct A* a) {
2 a->class->ping(a);
3 a->class->pong(a);
4}
5void bar () {
6 foo (new_A());
7 foo ((struct A *) new_B());
8}
a->class->ping(a);
a->class
in r[1]
)pc <- m[r[1] + 4]
Currently we support
We can convert the above to:
xxxxxxxxxx
21r[2] <- m[r[1] + 4] # ld 4(r1), r2
2pc <- r[2] # j(r2)
Can we join both instructions in one?
Double-indirect jump: jump to address stored in memory
xxxxxxxxxx
121ld (r5), r0 # r0 = a
2ld (r0), r1 # r1 = a->class
3deca r5 # allocate argument frame
4st r0, (r5) # store a as 1st argument in stack
5gpc $2, r6 # save return address
6j *0(r1) # a->class->ping(a)
7inca r5 # free argument frame
8deca r5 # allocate argument frame
9st r0, (r5) # store a as 1st argument in stack
10gpc $2, r6 # save return address
11j *4(r1) # a->class->pong(a)
12inca r5 # free argument frame
xxxxxxxxxx
121struct node {
2 int key;
3 int value;
4 struct node *next;
5};
6// ..
7void print_list(struct node *list){
8 while(list) {
9 printf("%d: %d\n", list->key, list->value);
10 list = list->next;
11 }
12}
The function print_list
is very specific:
Solution: function pointer
print_list
: what to do with each key-value pair. xxxxxxxxxx
131void follow_list(struct node *list,
2 void (*fn)(int, int)) {
3 while(list) {
4 fn(list->key, list->value);
5 list = list->next;
6 }
7}
8
9void print_element(int key, int value) {
10 printf("%10d: %10d\n", list->key, list->value)
11}
12// Print all elements in the list
13follow_list(list, print_element);
map receives a function and two lists, applies function to pairs of elements
(map + (list 1 4 3) (list 7 2 5) => (list 8 6 8))
(map (lambda (a b) (+ a b)) (list 1 4 3) (list 7 2 5))
(map max (list 1 4 3) (list 7 2 5)) => (list 7 4 5)
Other iterators:
(foldl + 0 (list 1 2 3))
(filter (lambda (a) (> a 3)) (list 1 2 3 4 5))
Other languages also include "lambda" calculation
xxxxxxxxxx
121void map(int (*fn)(int, int), int n, int *s0, int *s1, int *d) {
2 int i;
3 for (i = 0; i < n; i++) {
4 d[i] = fn(s0[i], s1[i])
5 }
6}
7
8int add(int a, int b) {
9 return a+b;
10}
11// ...
12map(add, 3, a, b, c);
xxxxxxxxxx
131int foldl(int (*fn)(int, int), int v, int *a, int n) {
2 int i;
3 for (i = 0; i < n; i++) {
4 v = fn(v, a[i])
5 }
6 return v;
7}
8
9int add(int a, int b) {
10 return a+b;
11}
12// ...
13printf("%d\n", foldl(add, 0, a, 3));
Semantics the same as simplified nested if statements
Assembly implementation: sequence of branches?
xxxxxxxxxx
121switch (i) {
2 case 0:
3 j = 10; break;
4 case 1:
5 j = 11; break;
6 case 2:
7 j = 12; break;
8 case 3:
9 j = 13; break;
10 default:
11 j = 14; break;
12}
Switch models a common idion
Switch statements have interesting restrictions
Case labels must be static, cardinal values
Case labels must be compared for equality to a single expression
Note that each case label represents a code starting line
Implementation can work with:
Computation can be more efficient
xxxxxxxxxx
141 void *jt[4] = { &&L0, &&L1, &&L2, &&L3 };
2 if (i < 0 || i > 3) goto DEFAULT;
3 else goto *jt[i];
4L0: j = 10;
5 goto CONTINUE;
6L1: j = 11;
7 goto CONTINUE;
8L2: j = 12;
9 goto CONTINUE;
10L3: j = 13;
11 goto CONTINUE;
12DEFAULT:j = 14;
13 goto CONTINUE;
14CONTINUE:
Jump table have a limitation:
Special consideration:
Strategy depends on case
Jump table implementation
xxxxxxxxxx
61switch (i) {
2 case 20: j = 10; break;
3 case 21: j = 11; break;
4 case 23: j = 13; break;
5 default: j = 14; break;
6}
xxxxxxxxxx
141 static const void* jt[4] =
2 { &&L20, &&L21, &&DEFAULT, &&L23 };
3 if (i < 20 || i > 23) goto DEFAULT;
4 goto *jt [i-20];
5L20: j = 10;
6 goto CONT;
7L21: j = 11;
8 goto CONT;
9L23: j = 13;
10 goto CONT;
11DEFAULT:
12 j = 14;
13 goto CONT;
14CONT:
xxxxxxxxxx
271foo: ld $i, r0 # r0 = &i
2 ld 0x0(r0), r0 # r0 = i
3 ld $0xffffffed, r1 # r1 = -19
4 add r0, r1 # r0 = i - 19
5 bgt r1, l0 # goto l0 if i>19
6 br default # goto default if i<20
7l0: ld $0xffffffe9, r1 # r1 = -23
8 add r0, r1 # r1 = i-23
9 bgt r1, default # goto default if i>23
10 ld $0xffffffec, r1 # r1 = -20
11 add r1, r0 # r0 = i-20
12 ld $jmptable, r1 # r1 = &jmptable
13 j *(r1, r0, 4) # goto jmptable[i-20]
14case20: ld $0xa, r1 # r1 = 10
15 br done # goto done
16case21: ld $0xb, r1 # r1 = 11
17 br done # goto done
18...
19default:ld $0xe, r1 # r1 = 14
20done: ld $j, r0 # r0 = &j
21 st r1, 0x0(r0) # j = r1
22...
23jmptable:
24 .long case20 # & (case 20)
25 .long case21 # & (case 21)
26 .long default # & (case 22)
27 .long case23 # & (case 23)
Existing double-indirect jump:
We need a new double-indirect jump: