In this segment we'll finish our Prg5.s example by looking at main, mostly via a series of in-lecture questions, like the prior segment.
Before we start with ILQs, however, there are a few details you should know
Recall that gcc sets up code that calls main. This setup code is the true starting point (e.g. where a _start label might be found) of the program. So main behaves like any other called function in setting up and tearing down a stack frame.
Like printf, a scanf call like scanf("%d%d%d", &valA, &valB, &valC) takes a first parameter that is the address of the format string (exactly, the address of the first % in this example). Unlike printf, however, the following parameters are also addresses, the addresses of each variable into which data is to be read. Scanf needs variable addresses because it needs to know where in memory to put data that it reads.
Also, gcc translates that scanf call to a call of __isoc99_scanf, which is a particular scanf version.
OK, so with that background, here are a few questions:
push pseudoinstruction.)
push {fp, lr}
add fp, sp, #4
sub sp, sp, #16
Answer 1
What do lines 62-64 do? (Note that they precede the scanf call.) Can you deduce from them what variables are stored in what order in the stack frame?
sub r3, fp, #20 sub r2, fp, #16 sub r1, fp, #12
Answer 2
scanf. Since the second, third and fourth parameters are valA valB and valC, respectively, this means that those variables appear in that order in the RTS, with valC on top. (And there is one more space not yet used, at fp - 8.)
ldr R0, .L7? What's that about? And, can you replace it with a version that doesn't use .L7?
Answer 3
.LC0 innto r0, since that's the first parameter scanf expects. It could have been done via ldr r0, =.LC0, letting the assembler create the address-holding word at .L7
Apparently gcc doesn't like relying on the assembler and prefers setting up its own address-holding words.
The call of scanf is obvious, but what are lines 67-70 after it doing? And, could you shorten them by one line? And, how did values get into those memory locations we're loading?
ldr r3, [fp, #-12] ldr r1, [fp, #-16] ldr r2, [fp, #-20] mov r0, r3
Answer 4
maxOf3 call, copying valA in r3, valB into r2, valC into r1, and then moving r3 into r0 so we have valA-C in r0-r2.
Changing r3 to r0 in the first ldr would make the final mov needless. But, as we've seen before, sometimes nonoptimizing compilers can be dumb. And, the values got into the memory locations via scanf, which used the addresses we passed to do strs into those variables when it read in data.
So, once we've called maxOf3, what does str r0, [fp, #-8] accomplish? And what variable is in mem location [fp #-8]?
Answer 5
max.
And lines 73-75?
ldr r1, [fp, #-8] ldr r0, =.LC1 bl printf
Answer 6
printf parameters and call printf. r1 is loaded from max. r0 gets the address of the "Max is %d ..." format string.
Apparently sometimes gcc does trust the assembler to make an address-holding memory word... Go figure.
And finally, these lines 76-79? (And yes, the first two are just more compiler stupidity -- that could and should have been mov r0, #0.) Make a reasonable guess as to what pop does.
mov r3, #0
mov r0, r3
sub sp, fp, #4
pop {fp, pc}
Answer 7
pop, which is a ldmfd sp!, .. shorthand, to copy the lr and old fp values into pc and fp, respectively, restoring the original fp and returning to whoever called main.