example of computed goto:
...
GO TO ( 10, 20, 30, 40 ), N
...
10 CONTINUE
...
20 CONTINUE
...
40 CONTINUE
If N equals one, then go to 10.
If N equals two, then go to 20.
If N equals three, then go to 30.
If N equals four, then go to 40.
What is the code generator of goto in the final state of compiling?
The most common way of compiling computed goto is a static jump table and an indirect branch instruction. For example (without -fPIC):
int test(int num) {
const void * const labels[] = {&&a, &&b, &&cl};
goto *labels[num];
a: return 1;
b: return 2;
cl: return 3;
}
Is going to be compiled as:
test(int): # #test(int)
movsxd rax, edi
jmp qword ptr [8*rax + .L__const.test(int).labels]
.Ltmp0: # Block address taken
mov eax, 1
ret
.Ltmp1: # Block address taken
mov eax, 3
ret
.Ltmp2: # Block address taken
mov eax, 2
ret
.L__const.test(int).labels:
.quad .Ltmp0
.quad .Ltmp2
.quad .Ltmp1
Related
I have this assembly problem where: Given the register AX=0425h. Write a program which adds the sum of digits of value 0425h and stores the sum in the same register AX.
I have no idea what to do in it. Can anyone help me solve this thing?
I tried to think of a solution and did not find anything :)
Given the register AX=0425h
The digits of this hexadecimal number are 0, 4, 2, and 5. The assignment wants you to sum these as in 0 + 4 + 2 + 5 = 11.
One possible solution is the following:
mov edx, eax ; -> DH=04h AL=25h
aam 16 ; 25h/16 -> AH=2 AL=5
add al, ah ; (5+2) -> AL=7
xchg al, dh ; -> DH=7 AL=04h
aam 16 ; 04h/16 -> AH=0 AL=4
add al, ah ; (4+0) -> AL=4
add al, dh ; (4+7) -> AL=11
cbw ; -> AX=11
The code works for any value AX=[0000h,FFFFh] producing AX=[0,60].
A solution that uses a loop and that can deal with any value EAX=[00000000h,FFFFFFFFh] producing EAX=[0,120]:
xor ecx, ecx ; TempResult = 0
More:
mov ebx, eax ; Copy to another temporary register where
and ebx, 15 ; we only keep the lowest digit
add ecx, ebx ; TempResult + LowestDigit
shr eax, 4 ; Shift the original digits to the right discarding the one(s) we already added to the TempResult
jnz More ; Only looping if more non-zero digits exist
mov eax, ecx ; EAX = TempResult
int array_list[] = {10, 11, 13, 18, 21, 23, 24, 17, 45};
int array_size = sizeof array_list / sizeof sample;
int index = 0; // index for while loop
int sum = 0; // accumulate the result
`for (current_size = array_size ; current_size > 0 ; current_size--)
{
while ( index < current_size)
{
if( array_list[index] is even )
{
sum += array_list[index];
}
index += 1;
}
} `
After converted, store the answer in the sum variable
So I have to convert it into x86 assembly language, and this is what I got so far
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode: DWORD
.data
sum DWORD 0
array_list DWORD 10,11,13,18,21,23,24,17,45
array_size = ($ - array_list) / TYPE array_list
.code
main PROC
mov eax, 0 ; sum
mov esi, 0 ; index
mov ecx, array_size
L1:
dec ecx
cmp esi, ecx
jl L2
jmp L5
L2:
cmp esi, ecx
jl L3
jmp L4
L3:
cmp array_list[esi], array_list[esi%2]
add eax, array_list[esi]
jmp L4
L4:
add esi, 1
jmp L1
L5:
mov sum, eax
INVOKE ExitProcess, 0
main ENDP
END main
For the array_size, I was trying to make sure that it is 40/4 = 10
I do not understand or know how to the for loop, so the first loop must be wrong what I wrote there.
Also, how do you do the if, where it said array_list[index] is even. Do I also need to declare the sample? Cuz it is used for the array_size. I really need help because I did not understand :(
The basic pattern equivalence for a for-loop is as follows:
C for-loop
for ( init; test; incr ) {
<loop-body>
}
C while-loop
init;
while ( test ) {
<loop-body>
incr;
}
That first loop in the assembly is following some different basic form: it has relocated the incr; portion in relation to the <loop-body>.
Surely you can see that in comparison with the following pattern:
init;
while ( test ) {
incr;
<loop-body>
}
that <loop-body> will see a different value for current_size each iteration (off by 1 increment, here -1) than in the C code, so it won't run the same as the C code, if that variable is ever consulted in the <loop-body>.
The test condition to continue the outer for in the assembly doesn't reflect the > 0 in the C code.
The % in the assembly code is doing array[index%2] whereas in the C code it is doing array[index]%2, which are clearly different concepts, so that wouldn't run the same (even if it were allowed like that).
Of course, x86 cannot do that compare instruction, because of the dual memory references. x86 requires one operand in register in order for the other to be a memory operand.
But that C code doesn't even require two array references there. Just one and than an even test on its value.
The C code requires an if-then which is missing in the assembly.
Let's convert the following while-loop (which matches the for-loop construct):
init;
while ( test ) {
<loop-body>
incr;
}
To assembly's if-goto-label style:
init;
loop1:
if ( ! test ) goto endLoop1;
<loop-body>
incr;
goto loop1;
endLoop1:
Here is a benchmark:
fn benchmark_or(repetitions: usize, mut increment: usize) -> usize {
let mut batch = 0;
for _ in 0..repetitions {
increment |= 1;
batch += increment | 1;
}
batch
}
fn benchmark_xor(repetitions: usize, mut increment: usize) -> usize {
let mut batch = 0;
for _ in 0..repetitions {
increment ^= 1;
batch += increment | 1;
}
batch
}
fn benchmark(c: &mut Criterion) {
let increment = 1;
let repetitions = 1000;
c.bench_function("Increment Or", |b| {
b.iter(|| black_box(benchmark_or(repetitions, increment)))
});
c.bench_function("Increment Xor", |b| {
b.iter(|| black_box(benchmark_xor(repetitions, increment)))
});
}
The results are:
Increment Or time: [271.02 ns 271.14 ns 271.28 ns]
Increment Xor time: [79.656 ns 79.761 ns 79.885 ns]
I get the same result if I replace or with and.
It's quite confusing as the or bench compiles to
.LBB0_5:
or edi, 1
add eax, edi
add rcx, -1
jne .LBB0_5
And the xor bench compiles to basically the same instructions plus two additional ones:
.LBB1_6:
xor edx, 1
or edi, 1
add eax, edi
mov edi, edx
add rcx, -1
jne .LBB1_6
Full Assembly code
Why is the difference so large?
This part of the function that uses XOR which you quoted:
.LBB1_6:
xor rdx, 1
or rsi, 1
add rax, rsi
mov rsi, rdx
add rcx, -1
jne .LBB1_6
Is only the "tail end" of an unrolled loop. The "meat" (the part that actually runs a lot) is this:
.LBB1_9:
add rax, rdx
add rdi, 4
jne .LBB1_9
rdx is set up to be 4 times increment - in a way that I would describe as "only a compiler could be this stupid", but it only happens outside the loop so it's not a complete disaster. The loop counter is advanced by 4 in every iteration (starting negative and counting up to zero, which is clever, redeeming the compiler somewhat).
This loop could be executed at 1 iteration per cycle, translating to 4 iterations of the source-loop per cycle.
The loop in the function that uses OR is also unrolled, this is the actual "meat" of that function:
.LBB0_8:
or rsi, 1
lea rax, [rax + 2*rsi]
lea rax, [rax + 2*rsi]
lea rax, [rax + 2*rsi]
lea rax, [rax + 2*rsi]
add rdi, 8
jne .LBB0_8
It's unrolled by 8, which might have been nice, but chaining lea 4 times like that really takes "only a compiler could be this stupid" to the next level. The serial dependency through the leas costs at least 4 cycles per iteration, translating to 2 iterations of the source-loop per cycle.
That explains a 2x difference in performance (in favour of the XOR version), not quite your measured 3.4x difference, so further analysis could be done.
Debugging my code in VS2015, I get to the end of the program. The registers are as they should be, however, on call ExitProcess, or any variation of that, causes an "Access violation writing location 0x00000004." I am utilizing Irvine32.inc from Kip Irvine's book. I have tried using call DumpRegs, but that too throws the error.
I have tried using other variations of call ExitProcess, such as exit and invoke ExitProcess,0 which did not work either, throwing the same error. Before, when I used the same format, the code worked fine. The only difference between this code and the last one is utilizing the general purpose registers.
include Irvine32.inc
.data
;ary dword 100, -30, 25, 14, 35, -92, 82, 134, 193, 99, 0
ary dword -24, 1, -5, 30, 35, 81, 94, 143, 0
.code
main PROC
;ESI will be used for the array
;EDI will be used for the array value
;ESP will be used for the array counting
;EAX will be used for the accumulating sum
;EBX will be used for the average
;ECX will be used for the remainder of avg
;EBP will be used for calculating remaining sum
mov eax,0 ;Set EAX register to 0
mov ebx,0 ;Set EBX register to 0
mov esp,0 ;Set ESP register to 0
mov esi,OFFSET ary ;Set ESI register to array
sum: mov edi,[esi] ;Set value to array value
cmp edi,0 ;Check value to temination value 0
je finsum ;If equal, jump to finsum
add esp,1 ;Add 1 to array count
add eax,edi ;Add value to sum
add esi,4 ;Increment to next address in array
jmp sum ;Loop back to sum array
finsum: mov ebp,eax ;Set remaining sum to the sum
cmp ebp,0 ;Compare rem sum to 0
je finavg ;Jump to finavg if sum is 0
cmp ebp,esp ;Check sum to array count
jl finavg ;Jump to finavg if sum is less than array count
avg: add ebx,1 ;Add to average
sub ebp,esp ;Subtract array count from rem sum
cmp ebp,esp ;Compare rem sum to array count
jge avg ;Jump to avg if rem sum is >= to ary count
finavg: mov ecx,ebp ;Set rem sum to remainder of avg
call ExitProcess
main ENDP
END MAIN
Registers before call ExitProcess
EAX = 00000163 EBX = 0000002C ECX = 00000003 EDX = 00401055
ESI = 004068C0 EDI = 00000000 EIP = 0040366B ESP = 00000008
EBP = 00000003 EFL = 00000293
OV = 0 UP = 0 EI = 1 PL = 1 ZR = 0 AC = 1 PE = 0 CY = 1
mov esp,0 sets the stack pointer to 0. Any stack instructions like push/pop or call/ret will crash after you do that.
Pick a different register for your array-count temporary, not the stack pointer! You have 7 other choices, looks like you still have EDX unused.
In the normal calling convention, only EAX, ECX, and EDX are call-clobbered (so you can use them without preserving the caller's value). But you're calling ExitProcess instead of returning from main, so you can destroy all the registers. But ESP has to be valid when you call.
call works by pushing a return address onto the stack, like sub esp,4 / mov [esp], next_instruction / jmp ExitProcess. See https://www.felixcloutier.com/x86/CALL.html. As your register-dump shows, ESP=8 before the call, which is why it's trying to store to absolute address 4.
Your code has 2 sections: looping over the array and then finding the average. You can reuse a register for different things in the 2 sections, often vastly reducing register pressure. (i.e. you don't run out of registers.)
Using implicit-length arrays (terminated by a sentinel element like 0) is unusual outside of strings. It's much more common to pass a function a pointer + length, instead of just a pointer.
But anyway, you have an implicit-length array so you have to find its length and remember that when calculating the average. Instead of incrementing a size counter inside the loop, you can calculate it from the pointer you're also incrementing. (Or use the counter as an array index like ary[ecx*4], but pointer-increments are often more efficient.)
Here's what an efficient (scalar) implementation might look like. (With SSE2 for SIMD you could add 4 elements with one instruction...)
It only uses 3 registers total. I could have used ECX instead of ESI (so main could ret without having destroyed any of the registers the caller expected it to preserve, only EAX, ECX, and EDX), but I kept ESI for consistency with your version.
.data
;ary dword 100, -30, 25, 14, 35, -92, 82, 134, 193, 99, 0
ary dword -24, 1, -5, 30, 35, 81, 94, 143, 0
.code
main PROC
;; inputs: static ary of signed dword integers
;; outputs: EAX = array average, EDX = remainder of sum/size
;; ESI = array count (in elements)
;; clobbers: none (other than the outputs)
; EAX = sum accumulator
; ESI = array pointer
; EDX = array element temporary
xor eax, eax ; sum = 0
mov esi, OFFSET ary ; incrementing a pointer is usually efficient, vs. ary[ecx*4] inside a loop or something. So this is good.
sumloop: ; do {
mov edx, [esi]
add edx, 4
add eax, edx ; sum += *p++ without checking for 0, because + 0 is a no-op
test edx, edx ; sets FLAGS the same as cmp edx,0
jnz sumloop ; }while(array element != 0);
;;; fall through if the element is 0.
;;; esi points to one past the terminator, i.e. two past the last real element we want to count for the average
sub esi, OFFSET ary + 4 ; (end+4) - (start+4) = array size in bytes
shr esi, 2 ; esi = array length = (end-start)/element_size
cdq ; sign-extend sum into EDX:EAX as an input for idiv
idiv esi ; EAX = sum/length EDX = sum%length
call ExitProcess
main ENDP
I used x86's hardware division instruction, instead of a subtraction loop. Your repeated-subtraction loop looked pretty complicated, but manual signed division can be tricky. I don't see where you're handling the possibility of the sum being negative. If your array had a negative sum, repeated subtraction would make it grow until it overflowed. Or in your case, you're breaking out of the loop if sum < count, which will be true on the first iteration for a negative sum.
Note that comments like Set EAX register to 0 are useless. We already know that from reading mov eax,0. sum = 0 describes the semantic meaning, not the architectural effect. There are some tricky x86 instructions where it does make sense to comment about what it even does in this specific case, but mov isn't one of them.
If you just wanted to do repeated subtraction with the assumption that sum is non-negative to start with, it's as simple as this:
;; UNSIGNED division (or signed with non-negative dividend and positive divisor)
; Inputs: sum(dividend) in EAX, count(divisor) in ECX
; Outputs: quotient in EDX, remainder in EAX (reverse of the DIV instruction)
xor edx, edx ; quotient counter = 0
cmp eax, ecx
jb subloop_end ; the quotient = 0 case
repeat_subtraction: ; do {
inc edx ; quotient++
sub eax, ecx ; dividend -= divisor
cmp eax, ecx
jae repeat_subtraction ; while( dividend >= divisor );
; fall through when eax < ecx (unsigned), leaving EAX = remainder
subloop_end:
Notice how checking for special cases before entering the loop lets us simplify it. See also Why are loops always compiled into "do...while" style (tail jump)?
sub eax, ecx and cmp eax, ecx in the same loop seems redundant: we could just use sub to set flags, and correct for the overshoot.
xor edx, edx ; quotient counter = 0
cmp eax, ecx
jb division_done ; the quotient = 0 case
repeat_subtraction: ; do {
inc edx ; quotient++
sub eax, ecx ; dividend -= divisor
jnc repeat_subtraction ; while( dividend -= divisor doesn't wrap (carry) );
add eax, ecx ; correct for the overshoot
dec edx
division_done:
(But this isn't actually faster in most cases on most modern x86 CPUs; they can run the inc, cmp, and sub in parallel even if the inputs weren't the same. This would maybe help on AMD Bulldozer-family where the integer cores are pretty narrow.)
Obviously repeated subtraction is total garbage for performance with large numbers. It is possible to implement better algorithms, like one-bit-at-a-time long-division, but the idiv instruction is going to be faster for anything except the case where you know the quotient is 0 or 1, so it takes at most 1 subtraction. (div/idiv is pretty slow compared to any other integer operation, but the dedicated hardware is much faster than looping.)
If you do need to implement signed division manually, normally you record the signs, take the unsigned absolute value, then do unsigned division.
e.g. xor eax, ecx / sets dl gives you dl=0 if EAX and ECX had the same sign, or 1 if they were different (and thus the quotient will be negative). (SF is set according to the sign bit of the result, and XOR produces 1 for different inputs, 0 for same inputs.)
I'm having a hard time grasping recursion. For example I have the following method. When the if statement returns true, I expect to return from this method. However looking at the method execution in Windbg and Visual Studio shows that the method continues to execute. I apologize for the generic question however your feedback would really be appreciated.
How is N decremented in-order to satisfy the if condition?
long factorial(int N)
{
if(N == 1)
return 1;
return N * factorial(N - 1);
}
compiling and disassembling the function you should get a disassembly similar to this
0:000> cdb: Reading initial command 'uf fact!fact;q'
fact!fact:
00401000 55 push ebp
00401001 8bec mov ebp,esp
00401003 837d0801 cmp dword ptr [ebp+8],1
00401007 7507 jne fact!fact+0x10 (00401010)
fact!fact+0x9:
00401009 b801000000 mov eax,1
0040100e eb13 jmp fact!fact+0x23 (00401023)
fact!fact+0x10:
00401010 8b4508 mov eax,dword ptr [ebp+8]
00401013 83e801 sub eax,1
00401016 50 push eax
00401017 e8e4ffffff call fact!fact (00401000)
0040101c 83c404 add esp,4
0040101f 0faf4508 imul eax,dword ptr [ebp+8]
fact!fact+0x23:
00401023 5d pop ebp
00401024 c3 ret
quit:
lets assume N == 5 when the function is entered ie [ebp+8] will hold 5
as long as [ebp+8] > 1 the jne will be taken
here you can see N being decremented (sub eax ,1)
the decremented N is again passed to the function fact (recursed without a return back to caller) the loop happens again and the decremented N is resent to fact this keeps on happening until the jne is not taken that is until N or [ebp+8] == 1
when N becomes 1 jne is not taken but jmp 401023 is taken
where it returns to the caller the caller being the function fact(int N)
that is it will return 40101c where the multiplication of eax of takes place and result is stored back in eax;
this will keep on happening until the ret points to the first call in main() see the stack below prior to executing pop ebp for the first time
0:000> kPL
ChildEBP RetAddr
0013ff38 0040101c fact!fact(
int N = 0n1)+0x23
0013ff44 0040101c fact!fact(
int N = 0n2)+0x1c
0013ff50 0040101c fact!fact(
int N = 0n3)+0x1c
0013ff5c 0040101c fact!fact(
int N = 0n4)+0x1c
0013ff68 0040109f fact!fact(
int N = 0n5)+0x1c
0013ff78 0040140b fact!main(
int argc = 0n2,
char ** argv = 0x00033ac0)+0x6f
I think the best way to grasp is to work through your code manually. Say you call factorial(4), what happens?4 is not equal to 1. Return 4 * factorial(4-1).
What is the return value of factorial 3? 3 is not equal to 1 return 3* factorial(3-1).
What is the return value of factorial 2? 2 is not equal to 1 return 2* factorial(2-1).
What is the return value of factorial 1? 1 equals 1 is true. Return 1. This is the base case. Now we move back up the recursion.
Return 1. This is factorial (2-1)
Return 2*1. This is factorial (3-1)
Return 3*2 this is factorial(4-1)
Return 4*6 this is factorial(4), the original call you made.
The idea is you have a function that has a base case (when n=1 return 1) and the function calls itself in way that moves the function towards the base case (factorial(n**-**1)).