Может ли соответствующий компилятор нарушить преобразования uint32_t -> int16_t -> int32_t?

Недавно мы обнаружили странное поведение в каком-то старом коде. Этот код работал много лет, но сломался на некоторых платформах (XBox 360, PowerPC), когда оптимизация компилятора была включена макс. Обычно я подозреваю неопределенное поведение.

Код выглядит примерно так:

#include <stdint.h>
uint32_t sign_extend16(uint32_t val)
{
   return (int32_t)(int16_t)val;
}

Это часть эмулятора, поэтому рассматриваемая операция не должна быть слишком странной. Обычно я ожидаю, что это будет учитывать только младшие 16 бит и расширять их знаком до 32 бит. Видимо, так он вел себя веками. На x86_64 GCC дает мне следующий результат:

0000000000000000 <sign_extend16>:
   0:   0f bf c7                movswl %di,%eax
   3:   c3                      retq

Однако из того, что я мог понять о стандарте, преобразование беззнакового в знаковое не определено, если невозможно представить значение беззнакового с типом со знаком.

Может ли компилятор предположить, что значение без знака должно быть в диапазоне [0, 32767] , поскольку любое другое значение будет неопределенным? В этом случае приведение к int16_t и еще одно приведение к int32_t ничего не даст. В этом случае разрешено ли компилятору преобразовать код в простой ход?

9
задан Maister 9 February 2012 в 23:16
поделиться