Assignment 1, Solutions
Part of
the homework for 22C:116, fall 2002
|
No solution given
No solution given
No solution given
O.M(P)There are several well-known approaches to implementing polymorphic objects; The fastest stores, in each object, pointers to the code of the polymorphic methods of that object's class. Objects with many polymorphic methods are therefore large. To minimize the object size, we can store, in each object, a pointer to the descriptor for the object's class, and store pointers to all of the polymorphic methods in the class descriptor.
Problem: Give code or pseudocode at the machine language level for the example call given above. Use the machine architecture you know best, or alternatively, give code in C (not C++) instead of machine code or machine-level pseudocode.
Pseudocode, by the way, gives details about some computation with an informal disregard for syntactic formality.
The following answers ignore multiple inheritance; this simplifies things considerably. If we assume each object contains pointers to its methods, the C code will be:
(*(O->M))(O,P)
Here, field M of structure O is a pointer to the function that implements the method, and this function takes 2 parameters. The first parameter is the structure itself, so the function has access to the object itself! If each object contains only a pointer to its class descriptor, where the descriptor has pointers to the relevant methods, the C code would be:
(*(O->Class->M))(O,P)
In assembly language, the former solution might be coded as follows on a typical RISC machine:
LOAD X1,O ;index register 1 gets a pointer to object O
LOAD X3,M(X1) ;index register 3 gets the contents of field M
LOAD X2,P ;index register 2 gets the parameter P
CALL (X3) ;call the function pointed to by X3
; we assume the called function expects its parameters in X1 and X2
The Question What is the difference between the information pushed on the stack by a typical function call and by response to a typical interrupt.
A function call need only push the return address and the contents of the few general purpose registers it will use during its execution. If it pushes registers it does not use, this is because either there is a very low-cost mechanism for saving all registers or because the compiler was unable to determine, at the time the calling sequence was finalized, what registers the function would need.
In contrast, an interrupt service routine must typically save not only the general purpose registers that it will be using, but also significant parts of the processor state that are hidden from user programmers. These hidden parts of the state might include such things as the memory management unit control registers and the state of the hardware protection mechanism. The interrupt entry and exit sequences are almost always hand-coded in assembly or machine language, so the amount of information saved is not a function of the intelligence of the compiler.