char e as regras usuais de conversão aritmética

Eu sei que esta pergunta foi feita e, aparentemente, respondeu um zilhão de vezes, mas eu não consigo corresponder às respostas da minha própria experiência.

O padrão C especifica que, para adição, "ambos os operandos devem ter tipo aritmético" (6.5.6.1). Tipos Arithemitc abrangem tipos inteiros e flutuantes (6.2.5.18) e finalmente tipos inteiros são char, short, int, long e long long que existem como tipos assinados e não assinados (6.2.5.4 e 6.2.5.6). De acordo com as regras da conversão aritmética usual "Se os dois operandos tiverem o mesmo tipo, nenhuma conversão adicional será necessária". Por enquanto, tudo bem.

Foi meu entendimento, como exemplificado aqui em "The C Book", que "[n] o aritmética é feita por C com uma precisão menor que int", que é onde a promoção integral é aplicada. Não consigo encontrar nenhuma referência a isso no padrão, parece ter visto isso várias vezes.

Como unsigned char é um tipo aritmético e as regras para conversões aritméticas usuais indicam que operandos do mesmo tipo não precisam de conversão, por que a necessidade de promoção integral?

Eu testei isso usando dois compiladores diferentes. Eu escrevi um programa simples que faz adição de char:

unsigned char a = 1;
unsigned char b = 2;
unsigned char c = a + b;

A plataforma de destino é um Atmel Mega8 uC usando uma arquitetura de 8 bits. Uma adição inteira exigiria, portanto, o uso de dois registradores se os operandos estivessem sujeitos à promoção integral.

Compilar isso usando o compilador imagecraft avr sem otimização e com opções de portabilidade ANSI C e restritas habilitadas produz este código assembly:

mov R16, R20
add R16, R18

Usando o avr-gcc (não estou ciente de um switch ANSI semelhante ao do gcc):

$ avr-gcc -O0 -mmcu=atmega8 -S -c main.c

O conjunto resultante:

ldd r25,Y+1
ldd r24,Y+2
add r24,r25
std Y+3,r24

O código resultante em ambos os casos opera em um único byte. Obtenho resultados semelhantes para bitwise | e & e lógico || e &&. Isso significa que o padrão permite operações aritméticas em tipos charecter sem promoção integral ou simplesmente significa que esses compiladores não são compatíveis com o padrão?

Adicional:

Acontece que tudo depende do tipo em que o resultado é armazenado. O exemplo mostrado acima só é verdadeiro quando o resultado é armazenado em um caractere e não depende do resultado da adição. Definir um para 0xFF eb para 1 produz exatamente o mesmo código assembly.

Se o tipo de c for alterado para unsigned int, o assembly resultante ficará assim:

mov R2,R20
clr R3
mov R16,R18 
clr R17
add R16,R2 
adc R17,R3 

Mesmo no caso em que o resultado pode ser mantido em um único byte, ou seja, a = 1 eb = 2.

questionAnswers(5)

yourAnswerToTheQuestion