While writing some C code, I decided to compile it to assembly and read it–I just sort of, do this from time to time–sort of an exercise to keep me thinking about what the machine is doing every time I write a statement in C.
Anyways, I wrote these two lines in C
asm(";move old_string[i] to new_string[x]");
new_string[x] = old_string[i];
asm(";shift old_string[i+1] into new_string[x]");
new_string[x] |= old_string[i + 1] << 8;
(old_string is an array of char, and new_string is an array of unsigned short, so given two chars, 42 and 43, this will put 4342 into new_string[x])
Which produced the following output:
#move old_string[i] to new_string[x]
movl -20(%ebp), %esi #put address of first char of old_string in esi
movsbw (%edi,%esi),%dx #put first char into dx
movw %dx, (%ecx,%ebx,2) #put first char into new_string
#shift old_string[i+1] into new_string[x]
movsbl 1(%esi,%edi),%eax #put old_string[i+1] into eax
sall $8, %eax #shift it left by 8 bits
orl %edx, %eax #or edx into it
movw %ax, (%ecx,%ebx,2) #?
(I’m commenting it myself, so I can follow what’s going on).
I compiled it with -O3, so I could also sort of see how the compiler optimizes certain constructs. Anyways, I’m sure this is probably simple, but here’s what I don’t get:
the first section copies a char out of old_string[i], and then movw’s it (from dx) to (%ecx,%ebx). Then the next section, copies old_string[i+1], shifts it, ors it, and then puts it into the same place from ax. It puts two 16 bit values into the same place? Wouldn’t this not work?
Also, it shifts old_string[i+1] to the high-order dword of eax, then ors edx (new_string[x]) into it… then puts ax into the memory! Wouldn’t ax just contain what was already in new_string[x]? so it saves the same thing to the same place in memory twice?
Is there something I’m missing? Also, I’m fairly certain that the rest of the compiled program isn’t relevant to this snippet… I’ve read around before and after, to find where each array and different variables are stored, and what the registers’ values would be upon reaching that code–I think that this is the only piece of the assembly that matters for these lines of C.
—
oh, turns out GNU assembly comments are started with a #.
Okay, so it was pretty simple after all.
I figured it out with a pen and paper, writing down each step, what it did to each register, and then wrote down the contents of each register given an initial starting value…
What got me was that it was using 32 bit and 16 bit registers for 16 and 8 bit data types…
This is what I thought was happening:
I didn’t get why it wrote it to memory twice though, or why it was using 32 bit registers (well, actually, my guess is that a 32 bit processor is way faster at working with 32 bit values than it is with 8 and 16 bit values, but that’s a totally uneducated guess), so I tried rewriting it:
This worked exactly the same.
I don’t know if this is an optimization or not (aside from taking one memory write out, which obviously is), but if it is, I know it’s not really worth it and didn’t gain me anything. In any case, I get what this code is doing now, thanks for the help all.