0

I am analysing how switch is converted from C to aseembly (GAS). I don't understand the GAS's trick how the 'lower edge' default is triggered.

There is a conditional jump for 'higher edge' :

cmpl    $8, %eax
ja  .L2

There is no conditional jump for 'lower edge', just a jump into jump table

So, how is the 'lower' default handled? e.g. when i input 10 - DEFAULT is displayed

-----------part of code where I think the trick is hidden (switch's range is 11-19, uneven numbers)

    call    scanf              #switch argument is in %eax
    movl    -4(%rbp), %eax 
    subl    $11, %eax
    cmpl    $8, %eax
    ja  .L2                   # higher edge default
    movl    %eax, %eax
    leaq    0(,%rax,4), %rdx
    leaq    .L4(%rip), %rax
    movl    (%rdx,%rax), %eax
    movslq  %eax, %rdx
    leaq    .L4(%rip), %rax
    addq    %rdx, %rax
    jmp *%rax

-----------below is source code

C code :

#include <stdio.h>

int main()
{
    int a;
    printf("Insert a number : ");
    scanf("%d",&a);

    switch(a)
    {
        case 11: 
            printf("ELEVEN\n");
            break;
        case 13:
            printf("THIRTEEN\n");
            break;
        case 15:
            printf("FIFTEEN\n");
            break;
        case 17:
            printf("SEVENTEEN\n");
            break;
        case 19:
            printf("NINETEEN\n");
            break;
        default:
            printf("DEFAULT\n");
            break;
    }
    return 0;
}

GAS code:

    .file   "switch1.c"
    .text
    .def    __main; .scl    2;  .type   32; .endef
    .section .rdata,"dr"
.LC0:
    .ascii "Insert a number : \0"
.LC1:
    .ascii "%d\0"
.LC2:
    .ascii "ELEVEN\0"
.LC3:
    .ascii "THIRTEEN\0"
.LC4:
    .ascii "FIFTEEN\0"
.LC5:
    .ascii "SEVENTEEN\0"
.LC6:
    .ascii "NINETEEN\0"
.LC7:
    .ascii "DEFAULT\0"
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $48, %rsp
    .seh_stackalloc 48
    .seh_endprologue
    call    __main
    leaq    .LC0(%rip), %rcx
    call    printf
    leaq    -4(%rbp), %rax
    movq    %rax, %rdx
    leaq    .LC1(%rip), %rcx
    call    scanf
    movl    -4(%rbp), %eax
    subl    $11, %eax
    cmpl    $8, %eax
    ja  .L2
    movl    %eax, %eax
    leaq    0(,%rax,4), %rdx
    leaq    .L4(%rip), %rax
    movl    (%rdx,%rax), %eax
    movslq  %eax, %rdx
    leaq    .L4(%rip), %rax
    addq    %rdx, %rax
    jmp *%rax
    .section .rdata,"dr"
    .align 4
.L4:
    .long   .L3-.L4
    .long   .L2-.L4
    .long   .L5-.L4
    .long   .L2-.L4
    .long   .L6-.L4
    .long   .L2-.L4
    .long   .L7-.L4
    .long   .L2-.L4
    .long   .L8-.L4
    .text
.L3:
    leaq    .LC2(%rip), %rcx
    call    puts
    jmp .L9
.L5:
    leaq    .LC3(%rip), %rcx
    call    puts
    jmp .L9
.L6:
    leaq    .LC4(%rip), %rcx
    call    puts
    jmp .L9
.L7:
    leaq    .LC5(%rip), %rcx
    call    puts
    jmp .L9
.L8:
    leaq    .LC6(%rip), %rcx
    call    puts
    jmp .L9
.L2:
    leaq    .LC7(%rip), %rcx
    call    puts
    nop
.L9:
    movl    $0, %eax
    addq    $48, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (GNU) 7.4.0"
    .def    printf; .scl    2;  .type   32; .endef
    .def    scanf;  .scl    2;  .type   32; .endef
    .def    puts;   .scl    2;  .type   32; .endef

Community
  • 1
  • 1
TonyCodie
  • 1
  • 1
  • When a conditional jump is not taken, the instruction immediately after the jump is executed. Is that what you are looking for? – fuz Apr 17 '20 at 12:36
  • when conditional jump is not taken, it means number is lower than 19, jump table is responsible for cases 11-19, so how cases 0-10 are handled ? – TonyCodie Apr 17 '20 at 12:39
  • 2
    Ah, that's what you mean. See how the code actually compares `a - 11 < 8` and does an unsigned comparison. So if `a` is less than 11, it appears as a very large number. This way, both bounds are checked with one conditional jump. – fuz Apr 17 '20 at 12:45
  • Thank you very much, that was one of my initial diagnosis, but I thougth it may be not the most 'optimal' one. Thanks again, and btw can you recommend sources for GAS learning? I looked through internet and most seem like some black magic, sorry for off-topic. – TonyCodie Apr 17 '20 at 13:06
  • 1
    GAS is just an assembler, so searching for it won't turn up anything useful. There's a bunch of stuff about x86 to be found though. Apart from that: read the Intel manuals and the optimisation guides. Frequently inspect compiler output for all the tricks the compiler comes up with and remember them. Read other people's code. – fuz Apr 17 '20 at 13:41
  • Found a pair of duplicates for the unsigned-compare range check trick. Also [What is the idea behind ^= 32, that converts lowercase letters to upper and vice versa?](https://stackoverflow.com/a/54585515) explains it for ASCII characters. – Peter Cordes Apr 17 '20 at 16:32

0 Answers0