Take the 2-minute tour ×

I have an asm loop guaranteed not to go over 128 iterations that I want to unroll with a PC-relative jump. The idea is to unroll each iteration in reverse order and then jump however far into the loop it needs to be. The code would look like this:

#define __mul(i) \
    "movq -"#i"(%3,%5,8),%%rax;" \
    "mulq "#i"(%4,%6,8);" \
    "addq %%rax,%0;" \
    "adcq %%rdx,%1;" \
    "adcq $0,%2;"

asm("jmp (128-count)*size_of_one_iteration" // I need to figure this jump out
    : "+r"(lo),"+r"(hi),"+r"(overflow)
    : "r"(a.data),"r"(b.data),"r"(i-k),"r"(k)
    : "%rax","%rdx");

Is something like this possible with gcc inline assembly?

share|improve this question
add comment

3 Answers

up vote 1 down vote accepted

In gcc inline assembly, you can use labels and have the assembler sort out the jump target for you. Something like (contrived example):

int max(int a, int b)
    int result;
    __asm__ __volatile__(
        "movl %1, %0\n"
        "cmpl %2, %0\n"
        "jeq  a_is_larger\n"
        "movl %2, %0\n"
        "a_is_larger:\n" : "=r"(result), "r"(a), "r"(b));
    return (result);

That's one thing. The other thing you could do to avoid multiplication is to make the assembler align the blocks for you, say, at a multiple of 32 bytes (I don't think the instruction sequence fits into 16 Bytes), like:

#define mul(i)                     \
    ".align 32\n"                  \
    ".Lmul" #i ":\n"               \
    "movq -" #i "(%3,%5,8),%%rax\n"\
    "mulq " #i "(%4,%6,8)\n"       \
    "addq %%rax,%0\n"              \
    "adcq %%rdx,%1\n"              \
    "adcq $0,%2\n"

This will simply pad the instruction stream with nop. If yo do choose not to align these blocks, you can still, in your main expression, use the generated local labels to find the size of the assembly blocks:

__asm__ ("imul $(.Lmul0-.Lmul1), %[label]\n"
__asm__ ("shlq $5, %[label]\n"
    "leaq .Lmulblkstart, %[dummy]\n"        /* this is PC-relative in 64bit */
    "jmp (%[dummy], %[label])\n"
    ".align 32\n"
    : ... [dummy]"=r"(dummy) : [label]"r"((128-count)))

And for the case where count is a compile-time constant, you can even do:

__asm__("jmp .Lmul" #count "\n" ...);

Little note on the end:

Aligning the blocks is a good idea if the autogenerated _mul() thing can create sequences of different lengths. For constants 0..127 as you use, that won't be the case as they all fit into a byte, but if you'll scale them larger it would go to 16- or 32-bit values and the instruction block would grow alongside. By padding the instruction stream, the jumptable technique can still be used.

share|improve this answer
add comment

Sorry I can't provide the answer in ATT syntax, I hope you can easily perform the translations.

If you have the count in RCX and you can have a label just after __mul(0) then you could do this:

; rcx must be in [0..128] range.
    imul ecx, ecx, -size_of_one_iteration ; Notice the multiplier is negative (using ecx is faster, the upper half of RCX will be automatically cleared by CPU)
    lea  rcx, [rcx + the_label] ; There is no memory read here
    jmp  rcx

Hope this helps.

EDIT: I made a mistake yesterday. I've assumed that referencing a label in [rcx + the_label] is resolved as [rcx + rip + disp] but it is not since there is no such addressing mode (only [rip + disp32] exists)

This code should work and additionally it will left rcx untouched and will destroy rax and rdx instead (but your code seems to not read them before writing to them first):

    lea  rax, [the_label] ; PC-relative addressing (There is no memory read here)
    add  rax, rdx
    jmp  rax 
share|improve this answer
add comment

This isn't a direct answer, but have you considered using a variant of Duff's Device instead of inline assembly? That would take the form of switch statement:

switch(iterations) {
  case 128: /* code for i=128 here */
  case 127: /* code for i=127 here */
  case 126: /* code for i=126 here */
  /* ... */
  case 1:   /* code for i=1 here*/
  default: die("too many cases");
share|improve this answer
I'm using a variant of Duff's device right now that works ok, but I posted this because I want to switch over to an asm-only way –  Chris Feb 7 '11 at 19:59
add comment

Your Answer


