Когда ARM g cc 9.2.1 заданы параметры командной строки -O3 -xc++ -mcpu=cortex-m0
[компилировать как C ++] и следующий код:
unsigned short adjust(unsigned short *p)
{
unsigned short temp = *p;
temp -= temp>>15;
return temp;
}
, получается разумный машинный код:
ldrh r0, [r0]
lsrs r3, r0, #15
subs r0, r0, r3
uxth r0, r0
bx lr
, что эквивалентно:
unsigned short adjust(unsigned short *p)
{
unsigned r0,r3;
r0 = *p;
r3 = temp >> 15;
r0 -= r3;
r0 &= 0xFFFFu; // Returning an unsigned short requires...
return r0; // computing a 32-bit unsigned value 0-65535.
}
Очень разумно. Последний "uxtw" можно было бы опустить в данном конкретном случае, но для компилятора, который не может доказать безопасность такой оптимизации, лучше проявить осторожность, чем рисковать возвратом значения за пределами диапазона 0-65535, что может полностью поглотить нисходящий код.
При использовании -O3 -xc -mcpu=cortex-m0
[идентичные параметры, за исключением компиляции как C, а не C ++], однако код меняется:
ldrh r3, [r0]
movs r2, #0
ldrsh r0, [r0, r2]
asrs r0, r0, #15
adds r0, r0, r3
uxth r0, r0
bx lr
unsigned short adjust(unsigned short *p)
{
unsigned r0,r2,r3;
r3 = *p;
r2 = 0;
r0 = ((unsigned short*)p)[r2];
r0 = ((int)r0) >> 15; // Effectively computes -((*p)>>15) with redundant load
r0 += r3
r0 &= 0xFFFFu; // Returning an unsigned short requires...
return temp; // computing a 32-bit unsigned value 0-65535.
}
Я знаю, что определенные угловые случаи для сдвига влево отличаются в C и C ++, но я думал, что сдвиги вправо одинаковы. Есть ли что-то иное в том, как работают сдвиги вправо в C и C ++, что заставило бы компилятор использовать другой код для их обработки? Версии до 9.2.1 генерируют немного меньше плохого кода в режиме C:
ldrh r3, [r0]
sxth r0, r3
asrs r0, r0, #15
adds r0, r0, r3
uxth r0, r0
bx lr
эквивалентно:
unsigned short adjust(unsigned short *p)
{
unsigned r0,r3;
r3 = *p;
r0 = (short)r3;
r0 = ((int)r0) >> 15; // Effectively computes -(temp>>15)
r0 += r3
r0 &= 0xFFFFu; // Returning an unsigned short requires...
return temp; // computing a 32-bit unsigned value 0-65535.
}
Не так плохо, как версия 9.2.1, но все же инструкция длиннее, чем могла бы быть прямая трансляция кода. При использовании 9.2.1 объявление аргумента как unsigned short volatile *p
устранит избыточную нагрузку p
, но мне любопытно, почему g cc 9.2.1 потребуется квалификатор volatile
, чтобы избежать избыточной нагрузки , или почему такая причудливая «оптимизация» происходит только в режиме C, а не в режиме C ++. Мне также несколько любопытно, почему g cc даже подумал бы о добавлении ((short)temp) >> 15
вместо вычитания temp >> 15
. Есть ли какой-то этап оптимизации, на котором это могло бы иметь смысл?