CS250 Final Exam Homework


1. Assume the virtual address 0x45674237 and a page size of 4KB and a 32 bit architecture.

    a) obtain the page number and the offset.
	Page number is the higher 20 bits: 0x45674 = 284276
	Offset is the lower 12 bits: 0x237 = 567
	
    b) Obtain the first-level index and the second-level index from the page number assuming that each of them is 10 bits long.
	First-level index is determined by the first 10 bits of the page number, and second by the second 
	10 bits.
			
		0x45674 =  0100 0101 01       10 0111 0100
		         (1st level index)  (2nd level index)
		
	first  level index: 01 0001 0101 = 0x115 = 227
	second level index: 10 0111 0100 = 0x274 = 628
	
    c) Draw a first-level page table and one second-level page table that will translate the above virtual address to address 0x83467237
First level page table Second level page table
0x3ff0x70000
 ...
0x1150x68000
 ...
0x000...
(0x3ff = 210-1)
0x3ff102
 ...
0x2740x83467
 ...
0x000...
(addr: 0x68000)
2. Assuming a 32 bit architecture and a 4KB page size, how many first level page tables are needed for a single process? How many second-level page tables are needed for a single process?
	Each process has one first level page table. Second level page tables are allocated as needed (up 
	to 210-1 tables).
	
3. Enumerate the page bits in the page table and what each of them is used for.
4. Enumerate the five uses of virtual memory used to speed up the execution of a program.
5. Explain how copy-on-write works during fork().
	The OS gives the child a copy of the parents memory that is read-only. Modifications cause page
	faults which are caught by the OS and a new copy of the page is created.
	
6. Explain how allocating memory initialized with zeros is accelerated by using Virtual Memory.
	Zero-initialized memory is all maps to one read-only memory page. Only when a page is modified 
	is a page allocated and actually filled with zeros.
	
7. Explain what is L1, L2, L3 cache.
	L1 cache is the closest to the processor, actually contained in the processor chip itself. This
	means that communication between the CPU and the cache is very fast (think CPU register fast).
	L2 cache is external to the processor.
	L3 cache is much larger than L1 and L2 cache but also much slower, and is built into the
	physical memory.
	
8. Explain what is locality of reference.
	Locality of reference refers to the frequency of access of the same requests. Higher
	locality means that there are many repetitions of the same request, low locality means there are 
	few repetitions.
	
9. Explain in what situations a memory cache will not give good results.
	This will happen when there are many cache misses. This occurs 
	when there is low locality of reference. The cache works well with repeated
	references rather than random references.
	
10. Explain how it is possible in a direct mapping memory cache that two items that are frequently used cannot be stored in the cache simultaneously.
	In direct mapping memory, cache segments of memory are assigned-mapped to certain parts of the
	cache. If two pieces of data map to the same block in the cache they will constantly vie for
	that space and never both be in the cache at the same time.
	
11. Explain how buffers are used to reduce the numbers of system calls and device activity in a computer system.
	Buffers are used to reduce the number of system calls. For example a cached read operation will 
read first a large chunk of data in a single system call and then use the data in the buffer
to satisfy subsequent cached read operations. The same for cached write operations.
12. Explain the steps of a interrupt.
	Interrupts is an asynchronous reaction based behavior of the CPU. When the interrupt line is set
	to high the CPU immediatly stops and jumps to the handler for that interrupt. In doing this
	the CPU first backs up all registers and looks up the interrupt in the interrupt vector, running
	the handler. The registers are then restored and the CPU continues executing its previous task.
	
13. Explain the steps to process a page fault.
	The page is found to not be in RAM, and a page fault is thrown. The CPU saves the registers and
	runs the appropriate interrupt handler. The handler generates a segfault signal if the page does
	not belong to the process, otherwise it loads the page, finding a free page or storing a page in
	memory to disk and loading it to that page. Finally it clears the modified and access bits and
	returns to the previous task and retries the instruction that generated the page fault.
	
14.In x86-64 Assembly language implement the function int addarray(int n, int * array) that add all the elements of the array 
passed as parameter. n is the length of the array. 

.text
.globl addarray
.type addarray, @function
# int addarray(int n, int * array) {
# // n = %rdi array = %rsi
addarray: # // i = %rdx sum = %rax
#
movq $0,%rdx # i=0 ;
movl $0,%eax # sum=0 ;
#
while: cmpq %rdx,%rdi # while (i<n) { // (n-i>0)
jle afterw #
#
addl (%rsi),%eax # sum += *array;
addq $1,%rdx # i++ ;
addq $4,%rsi # array++ ;
jmp while # }
afterw: ret # return sum; }

15. In x86-64 Assembly language implement a program "maxmin" that prompts two integer numbers fom stdin and then displays the maximum, minimum and average of both: numbers. Here is an example of the usage:

bash> maxmin
a=5
b=8
max=8
min=5

#maxmin.s
.data
.comm a,8
.comm b,8
str1: .string "a="
str2: .string "b="
str3: .string "%d"
str4: .string "max=%d\n"
str5: .string "min=%d\n"

.text
.globl main

.type main, @function
main:
movq $str1, %rdi
movq $0, %rax
call printf

movq $a, %rdx
movq $str3, %rax
movq %rdx, %rsi
movq %rax, %rdi
movq $0, %rax
call scanf

movq $str2, %rdi
movq $0, %rax
call printf

movq $b, %rdx
movq $str3, %rax
movq %rdx, %rsi
movq %rax, %rdi
movq $0, %rax
call scanf

movq $a, %rdi
movq $b, %rsi
movq (%rdi), %rax
movq (%rsi), %rdx
cmpq %rdx,%rax
jg tag

movq $a, %rdi
movq $b, %rsi
movq (%rsi), %r8
movq (%rdi), %r9
movq %r9, (%rsi)
movq %r8, (%rdi)

tag:
movq $a, %rdx
movq (%rdx), %rsi
movq $str4, %rdi
movq $0, %rax
call printf

movq $b, %rdx
movq (%rdx), %rsi
movq $str5, %rdi
movq $0, %rax
call printf

ret
16. From your lab7 compiler, complete the following code for function definition. Add comments describing your code.
   function:
     var_type WORD
	    {
			 fprintf(fasm, "\t.text\n");      // part of the executable section of the file
			 fprintf(fasm, ".globl %s\n", $2);// declare a global function where the name is
			 fprintf(fasm, "%s:\n", $2);      // the string contained in WORD ($2-2nd part)
			 // save callee saved registers that might change to the stack.
			 fprintf(fasm, "# Save registers\n");
			 fprintf(fasm, "\tpushq %%rbx\n");
			 fprintf(fasm, "\tpushq %%r10\n");
			 fprintf(fasm, "\tpushq %%r13\n");
			 fprintf(fasm, "\tpushq %%r14\n");
			 fprintf(fasm, "\tpushq %%r15\n");
			 // Reserve space for args and locals
			 fprintf(fasm, "\tsubq $%d,%%rsp\n", 8*MAX_LOCALS); 
			 nlocals = 0;
			 top=0;
	
		}
		LPARENT arguments RPARENT
	    {
			 int i;
			 fprintf(fasm, "\t#Save arguments\n");
			 for (i=0; i<nlocals; i++) {  // for each passed in argument
			     fprintf(fasm, "\tmovq %%%s,%d(%%rsp)\n",
					 regArgs[i], 8*(MAX_LOCALS-i) ); // move from register to reserved 
					                                 //   space on stack
			 }
	    }
		compound_statement
		{
			 // clean up and return in case return was not explicitly in program.
			 fprintf(fasm, "\taddq $%d,%%rsp\n", 8*MAX_LOCALS); // Restore space in stack for
			 fprintf(fasm, "# Restore registers\n");            //    local vars
			 fprintf(fasm, "\tpopq %%r15\n");
			 fprintf(fasm, "\tpopq %%r14\n");
			 fprintf(fasm, "\tpopq %%r13\n");
			 fprintf(fasm, "\tpopq %%r10\n");
			 fprintf(fasm, "\tpopq %%rbx\n");
			 fprintf(fasm, "\tret\n", $2);
	    };
	
17. From your lab7 compiler, complete the following code for addition and substraction. Add comments describing your code.
	// to implement order of operations an addition_expr is categorized
	// as a multiplicative expression or the addition or subtraction of 
	// an additive expression and a multiplicative expression
	// (recursion results in left to right evaluation).
	additive_expr:
          multiplicative_expr
	  | additive_expr PLUS multiplicative_expr {
		fprintf(fasm,"\n\t# +\n");
		if (top<nregStk) {  // if can fit in working registers
			fprintf(fasm, "\taddq %%%s,%%%s\n", // pops two elements from stack, and puts
			    regStk[top-1], regStk[top-2]);  // their sum on the top of the stack.
			top--;                              // total change in stack size -1.
		}
	  }
	  | additive_expr MINUS multiplicative_expr {
	  	fprintf(fasm,"\n\t# -\n");
		if (top<nregStk) {
			fprintf(fasm,"\tsubq %%%s, %%%s\n", // pops two elements from the stack, and puts
			    regStk[top-1], regStk[top-2]);  // their difference on the top of the stack.
			top--;                              // total change in stack size -1.
		}
	  };
      
18. From your lab7 compiler, complete the following code for WHILE loop. Add comments describing your code.
	statement:
		assignment SEMICOLON
		...
		| WHILE LPARENT {
			// start here: reserves a label for this while loop.
			$1=nlabel;
			nlabel++;
			// label of last loop entered so you can break out of it.
			// broken as you can call break after you leave an internal loop but was sufficient
			// for required cases. Alternative would be to have a loop "stack" per function scope.
			fprintf(fasm, "continue_%d:\n", $<my_nlabel>1); // continue loop from here
		} expression RPARENT {                       // generate code to eval expression
			                                     // falls through to here
			fprintf(fasm, "\tcmpq $0, %%rbx\n"); // if the top of the stack is zero, was false
			fprintf(fasm, "\tje break_%d\n", $<my_nlabel>1);// leave loop.
			top--;
		} statement {                                // otherwise continue running loop
			// act3
			fprintf(fasm, "\tjmp continue_%d\n", $<my_nlabel>1);
			fprintf(fasm, "break_%d:\n", $<my_nlabel>);
		}
		...
		;

19. From your lab7 compiler, complete the following code for assignment. Add comments describing your code.
	assignment:
		// NOTE: atm, a > (b=c) not supported
		WORD EQUAL expression {
			char * id = $1; // get the variables name.
			int i = lookupLocalVar(id); // look it up in the var list.
			if (i>=0) {
				// local variable, so assign to stack at proper offset.
				fprintf(fasm, "\t#Assign to Local var %s\n", id);
				fprintf(fasm, "\tmovq %%%s, %d(%%rsp)\n",
				regStk[top-1], 8*(MAX_LOCALS-i) );
			} else {
				// global variable, assign to memory address labeled by id.
				fprintf(fasm, "\t#Assign to Global var %s\n", id);
				fprintf(fasm, "\tmovq %%%s, %s\n",regStk[top-1], id);
			}
			top--;
	 	}
	 	| WORD LBRACE expression RBRACE EQUAL expression {
			int i, type;
			char * id = $1;
			i = lookupLocalVar(id);
			// a[loc]=val; stack: ... loc val top
			fprintf(fasm,"\n\t# %s[x]=a\n",id);
			if (i>=0) { // if in the local var list
				// get type.
				type = local_vars_type[i];
			 	fprintf(fasm, "\t#Push Local var %s\n", id);

				// shift "loc" by appropriate amount so that
				// "loc" (top-2) is the memory offset.
				if(type==charstar_t) {
				} else if(type==charstarstar_t) {
					fprintf(fasm, "\tshlq $8, %%%s\n",regStk[top-2]);
				} else if(type==longstar_t) {
					fprintf(fasm, "\tshlq $8, %%%s\n",regStk[top-2]);
				} else {
					fprintf(stderr, "\n%s (%d) is not a pointer.\n",id,type);
					exit(0);
				}
				// move "val" (top-1) onto the memory address on the stack+offset
				//   where the shifted "loc" (top-2) specifies the offset.
				fprintf(fasm, "\taddq %d(%%rsp), %%%s\n",
					8*(MAX_LOCALS-i), regStk[top-2] );
				if(type==charstar_t) {
					// use temperary register to avoid overwriting adjacent bytes
					fprintf(fasm, "\tmovq %%%s, %%r12\n",
						regStk[top-1]);
					fprintf(fasm, "\tmovb %%r12b, (%%%s)\n",
						regStk[top-2]);
				} else {
					fprintf(fasm, "\tmovq %%%s, (%%%s)\n",
						regStk[top-1], regStk[top-2] );
				}
				top-=2;
			} else {
				// behavior similar to above method, however uses
				// a global variable instead of a local variable
				// to store the base memory address
				i = lookupGlobalVar(id);
				type = global_vars_type[i];
			 	fprintf(fasm, "\t#Push Local var %s\n", id);
				if(type==charstar_t) { // size is 1, do nothing
				} else if(type==charstarstar_t) {
					fprintf(fasm, "\tshlq $8, %%%s\n",regStk[top-2]);
				} else if(type==longstar_t) {
					fprintf(fasm, "\tshlq $8, %%%s\n",regStk[top-2]);
				} else {
					fprintf(stderr, "\n%s %d is not a pointer.\n",id,type);
					exit(0);
				}
				fprintf(fasm, "\taddq %s, %%%s\n",
						id, regStk[top-2]);
				if(type==charstar_t) {
					fprintf(fasm, "\tmovq %%%s, %%r12\n",
						regStk[top-1]);
					fprintf(fasm, "\tmovb %%r12b, (%%%s)\n",
						regStk[top-2]);
				} else {
					fprintf(fasm, "\tmovq %%%s, (%%%s)\n",
						regStk[top-1], regStk[top-2] );
				}
				top-=2;
			}
	 	}
	 ;