Comportamiento extraño con asamblea en línea GCC
-
15-11-2019 - |
Pregunta
Cuando enline el montaje en GCC, me encuentro regularmente por tener que agregar bloques de ASM vacíos para mantener vivos a las variables en bloques anteriores, por ejemplo:
asm("rcr $1,%[borrow];"
"movq 0(%[b_],%[i],8),%%rax;"
"adcq %%rax,0(%[r_top],%[i],8);"
"rcl $1,%[borrow];"
: [borrow]"+r"(borrow)
: [i]"r"(i),[b_]"r"(b_.data),[r_top]"r"(r_top.data)
: "%rax","%rdx");
asm("" : : "r"(borrow) : ); // work-around to keep borrow alive ...
Otro ejemplo de la rareza es que el código a continuación funciona bien sin optimizaciones, pero con -O3, SEG-FAILS:
ulong carry = 0,hi = 0,qh = s.data[1],ql = s.data[0];
asm("movq 0(%[b]),%%rax;"
"mulq %[ql];"
"movq %%rax,0(%[sb]);"
"movq %%rdx,%[hi];"
: [hi]"=r"(hi)
: [ql]"r"(ql),[b]"r"(b.data),[sb]"r"(sb.data)
: "%rax","%rdx","memory");
for (long i = 1; i < b.size; i++)
{
asm("movq 0(%[b],%[i],8),%%rax;"
"mulq %[ql];"
"xorq %%r10,%%r10;"
"addq %%rax,%[hi];"
"adcq %%rdx,%[carry];"
"adcq $0,%%r10;"
"movq -8(%[b],%[i],8),%%rax;"
"mulq %[qh];"
"addq %%rax,%[hi];"
"adcq %%rdx,%[carry];"
"adcq $0,%%r10;"
"movq %[hi],0(%[sb],%[i],8);"
"movq %[carry],%[hi];"
"movq %%r10,%[carry];"
: [carry]"+r"(carry),[hi]"+r"(hi)
: [i]"r"(i),[ql]"r"(ql),[qh]"r"(qh),[b]"r"(b.data),[sb]"r"(sb.data)
: "%rax","%rdx","%r10","memory");
}
asm("movq -8(%[b],%[i],8),%%rax;"
"mulq %[qh];"
"addq %%rax,%[hi];"
"adcq %%rdx,%[carry];"
"movq %[hi],0(%[sb],%[i],8);"
"movq %[carry],8(%[sb],%[i],8);"
: [hi]"+r"(hi),[carry]"+r"(carry)
: [i]"r"(long(b.size)),[qh]"r"(qh),[b]"r"(b.data),[sb]"r"(sb.data)
: "%rax","%rdx","memory");
Creo que tiene que ver con el hecho de que está utilizando tantos registros.¿Hay algo que me esté perdiendo aquí o es la asignación del registro, realmente un buggy con la asamblea en línea GCC?
Solución
What you're missing is that GCC's optimiser will assume that the only side-effect of an asm
block is to change the output operands. If those operands are not subsequently used, it may assume that the asm
block in unnecessary and can be deleted.
e.g. in your first example, if borrow
is not subsequently used, it is free to assume that there is no point in including the asm
block at all, because its only side-effect is to update a variable that is never used again. And in the second example, if hi
and carry
are not used again after the code you've shown, it will probably infer that it can delete pretty much everything!
You can tell GCC that your inline assembly blocks should not be deleted by writing asm volatile(...)
instead of just asm(...)
.
For more detail about this, see http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html (about half-way down the page).