Por que o compilador não usa diretamente LSR

10

Oi Eu estive trabalhando em um projeto usando um Arduino Uno (então ATmega328p), onde o tempo é muito importante e, portanto, eu queria ver em quais instruções o compilador estava convertendo meu código. E aí eu tenho um uint8_tque eu desloco um pouco para a direita em cada iteração usando data >>= 1e parece que o compilador traduziu isso em 5 instruções ( dataestá em r24):

mov     r18, r24
ldi     r19, 0x00
asr     r19
ror     r18
mov     r24, r18

Mas se eu olhar para a documentação do conjunto de instruções, vejo uma instrução que faz exatamente isso: lsr r24

Eu negligencio algo ou por que o compilador não está usando isso também? Os registradores r18e r19não são usados ​​em nenhum outro lugar.

Estou usando um Ardunio, mas se estiver correto, basta usar o avr-gcccompilador normal . Este é o código (aparado) que gera a sequência:

ISR(PCINT0_vect) {
    uint8_t data = 0;
    for (uint8_t i = 8; i > 0; --i) {
//        asm volatile ("lsr %0": "+w" (data));
        data >>= 1;
        if (PINB & (1 << PB0))
            data |= 0x80;
    }
    host_data = data;
}

Tanto quanto posso ver, o Ardunino IDE está usando o compilador AVR gcc fornecido pelo sistema, versão 6.2.0-1.fc24. Ambos são instalados através do gerenciador de pacotes, portanto, devem estar atualizados.

xZise
fonte
1
A montagem não parece corresponder ao código C.
Eugene Sh.
Bem, eu compilei usando o Ardunio IDE e depois usei avr-objdumpno arquivo elf ... O que parece não corresponder?
xZise 7/02
1
@Eugene Sh .: Ele faz correspondem ao código C. Corresponde apenas à linhadata >>= 1;
Coalhada
1
Este é um dos casos em que "usar turnos em vez de divisão" é o conselho errado. Se você fizer / = 2, o compilador irá gerar lsr r24; (dica: experimente o gcc para brincar com a geração de código asm)
PlasmaHH
Qual compilador? Qual processador? Realmente deve ser óbvio que essas informações são necessárias para que a pergunta faça sentido.
Olin Lathrop

Respostas:

18

De acordo com a especificação da linguagem C, qualquer valor cujo tamanho seja menor que o tamanho int(depende do compilador específico; no seu caso inttem 16 bits de largura) envolvido em qualquer operação (no seu caso >>) é upcast para um intantes da operação.
Esse comportamento do compilador é chamado de promoção inteira .

E foi exatamente isso que o compilador fez:

  • r19 = 0 é o MSByte do valor inteiro promovido de data.
  • (r19, r18) representa o valor total promovido do número inteiro dataque é então deslocado para a direita um bit por asr r19e ror 18.
  • O resultado é então implicitely elenco de volta para sua uint8_tvariável data:
    mov r24, r18, ou seja, o MSByte em R19 é jogado fora.

Editar:
é claro que o complier pode otimizar o código.
Tentando reproduzir o problema, descobri que, pelo menos com a versão 4.9.2 do avr-gcc, o problema não ocorre. Ele cria código muito eficiente, ou seja, a linha C data >>= 1;é compilada em apenas uma única lsr r24instrução. Talvez você esteja usando uma versão do compilador muito antiga.

Coalhada
fonte
2
Não é um desperdício total, porque às vezes você precisa do código não otimizado para depuração no nível do assembler. Você ficará muito satisfeito se tiver um código não otimizado.
Requeijão
3
Se bem me lembro, -mint8 é a bandeira para fazer números inteiros de 8 bits. No entanto, isso tem muitos efeitos colaterais indesejados. Desculpe, não consigo me lembrar o que eram agora, mas nunca usei a bandeira por causa deles. Passei muito tempo comparando o avr-gcc com um compilador comercial há muitos anos.
7117 Jon
1
Ah, está certo, o padrão C exige que os números inteiros tenham pelo menos 16 bits, portanto, usar -mint8 quebra todas as bibliotecas.
7117 Jon
9
Nigel Jones disse em "Código C eficiente para microcontroladores de 8 bits" algo como: "... As regras de promoção inteira de C são provavelmente o crime mais hediondo cometido contra aqueles que trabalham no mundo de 8 bits" ...
Dirceu Rodrigues Jr
1
@Jonas Wielicki: a melhor solução para o problema é usar um compilador melhor. Por exemplo, com o avr-gcc versão 4.9.2, não consigo reproduzir o problema: para a linha de código C, d >>= 1;recebo apenas uma única lsr r24instrução. Talvez o xZise esteja usando uma versão do compilador muito antiga.
Coalhada