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?

Foi útil?

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).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top