Any way to stop duplication of code in this assembly code

1

I have only just started to learn the assembly language for the x86 architecture. I am doing some exercises based on conditional branching. At this point in time, I have been introduced to the JMP, JZ and JNZ instructions as far as branching of code is concerned. This is the link for the exercise. Point 5 says if input is given as 0, the program screws up. That's fair, since dec eax would result in the eax register to hold 0xffffffff. I need to fix this. Here is what I have come up with - Add a dummy instruction add eax, 0h just to check if this results in the zero-flag being set, and if set jump to a place in the code, which prints out the required value and exit the program.

start:
    ; The program begins here:

    call    read_hex
    add     eax, 0h; my code
    jz      lb2; my code
    mov     ecx,1

lb1:
    add     ecx,ecx
    dec     eax
    jnz     lb1

    mov     eax,ecx
    jmp     lb3

lb2:
    inc     eax
    call    print_eax
    ; Exit the process:
    push    0
    call    [ExitProcess]

lb3:
    call    print_eax
    ; Exit the process:
    push    0
    call    [ExitProcess]

The thing that's making me uncomfortable is that I have duplicate code in lb2 and lb3 and I am not quite sure if there is any way around this.

assembly
x86
asked on Stack Overflow Jan 4, 2015 by user1720897 • edited Apr 15, 2019 by 1201ProgramAlarm

2 Answers

2
start:
    ; The program begins here:

    call    read_hex
    add     eax, 0h; my code
    jz      lb2; my code
    mov     ecx,1

lb1:
    add     ecx,ecx
    dec     eax
    jnz     lb1

    mov     eax,ecx
    jmp     lb3

lb2:
    inc     eax

lb3:
    call    print_eax
    ; Exit the process:
    push    0
    call    [ExitProcess]
answered on Stack Overflow Jan 4, 2015 by Marat Talipov
1

If you wanted to keep your left-shift one bit at a time (with add same,same) loop, simplify the handling of the count=0 case to just skipping over the shift loop to the mov instruction, allowing you to remove the jmp lb3

test eax,eax is the best way to set ZF according to whether EAX is zero or not. (Not add eax,0)

start:
    ; The program begins here:
    call    read_hex

    mov     ecx,1
    test    eax, eax
    jz     no_shift

shift_loop:                            ; like shl  ecx, eax  without wrapping the shift count
    add     ecx,ecx
    dec     eax
    jnz     shift_loop
no_shift:
    mov     eax,ecx

    call    print_eax
    ; Exit the process:
    push    0
    call    [ExitProcess]

See Why are loops always compiled into "do...while" style (tail jump)? for more about handling loops with the branch at the bottom, even for cases where the loop might need to run 0 times.

In your case, there's no benefit to having a data dependency on EAX for the EAX=0 case. (inc eax). This defeats some of the benefit of branch prediction + speculative execution for that case; using mov eax, ecx to make EAX=1 means code after the branch can use EAX before the old value of EAX was even ready for the branch to actually check the prediction. (If it was correctly predicted.)


You don't need to branch at all for any of this. x86 has shift instructions that make it very easy to create 1 << n.

start:
    ; The program begins here:

    call    read_hex

    xor     edx, edx          ; edx = 0
    bts     edx, eax          ; edx |= 1<<eax
    mov     eax, edx

    call    print_eax
    push    0
    call    [ExitProcess]

This is even more efficient than using a variable count shl (3 uops on Sandybridge-family), but with exactly equivalent semantics for wrapping the shift count:

mov    ecx, eax
mov    eax, 1
shl    eax, cl

If you really need large inputs to shift the bit all the way out and leave zero, you could branch for that or you could consider using SSE2 or MMX integer shifts, which do saturate the shift count instead of wrapping it. (i.e. count & 0x3f)


answered on Stack Overflow Apr 15, 2019 by Peter Cordes

User contributions licensed under CC BY-SA 3.0