Comportamento estranho com montagem inline do gcc
-
15-11-2019 - |
Pergunta
Ao incorporar o assembly no gcc, regularmente tenho que adicionar blocos ASM vazios para manter as variáveis ativas nos blocos anteriores, por exemplo:
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 ...
Outro exemplo de estranheza é que o código abaixo funciona muito bem sem otimizações, mas com -O3 ele falha:
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");
Acho que tem a ver com o fato de estar usando tantos registros.Há algo que estou faltando aqui ou a alocação de registro está realmente problemática com o assembly embutido do gcc?
Solução
O que está faltando é que o otimizador do GCC assumirá que o único efeito colateral de um asm
bloco é alterar os operandos de saída.Se esses operandos não forem utilizados posteriormente, pode-se assumir que o asm
bloquear desnecessário e pode ser excluído.
por exemplo.no seu primeiro exemplo, se borrow
não for utilizado posteriormente, é livre presumir que não faz sentido incluir o asm
bloquear, porque seu único efeito colateral é atualizar uma variável que nunca mais será usada.E no segundo exemplo, se hi
e carry
não forem usados novamente após o código que você mostrou, provavelmente inferirá que pode excluir praticamente tudo!
Você pode dizer ao GCC que seus blocos de montagem embutidos não devem ser excluídos escrevendo asm volatile(...)
em vez de apenas asm(...)
.
Para mais detalhes sobre isso, veja http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html (mais ou menos na metade da página).