Если вам интересно, почему бы не movzbw (%rdi), %ax
для short
, то это потому, что запись в 8-битные и 16-битные частичные регистры должна сливаться с предыдущими старшими байтами.
Запись 32-битного регистра, такого как EAX, неявно распространяется на ноль в полный RAX, избегая ложной зависимости от старого значения RAX или любого слияния ALU. ( Почему инструкции x86-64 для 32-битных регистров обнуляют верхнюю часть полного 64-битного регистра? )
«Нормальный» способ загрузки байта наx86 с movzbl
или movsbl
, так же, как на RISC-машине, такой как ARM ldrb
или ldrsb
, или MIPS lbu
/ lb
.
странный CISCGCC обычно избегает слияния со старым значением, которое заменяет только младшие биты, например movb (%rdi), %al
. Почему GCC не использует частичные регистры? Clang более безрассуден и будет чаще писать частичные регистры, а не просто читать их для магазинов. Вы можете хорошо видеть, что лязг загружается в %al
и сохраняется, когда dst_t
равен signed char
.
Если вам интересно, почему бы не movsbl (%rdi), %eax
(знак-расширение)
Значение source не имеет знака, поэтому расширение нуля (не расширение знака) является правильным способом его расширения в соответствии с семантикой C. Чтобы получить movsbl
, вам понадобится return (int)(signed char)c
.
В *dp = (dst_t)*sp;
приведение к dst_t
уже подразумевается от назначения *dp
.
Диапазон значений для unsigned char
равен 0..255 (для x86, где CHAR_BIT = 8).
Расширение нуля до signed int
может привести к диапазону значений от 0..255
, то есть, сохраняя каждое значение как неотрицательные целые числа со знаком.
Расширение знака до signed int
приведет к диапазону значений от -128..+127
, изменяя значение unsigned char
values> = 128. конфликтует с семантикой C для расширения конверсии с сохранением значений.
Не должно ли оно соответствовать dst_t
?
Должно расширяться как минимум в ширину dst_t
. Оказывается, расширение до 64-битного с использованием movzbl
(с верхними 32 битами, обрабатываемыми неявным расширением нуля, записывающим 32-битный регистр) является наиболее эффективным способом расширения вообще.
Хранение*dp
- хорошая демонстрация того, что asm предназначен для dst_t
с шириной, отличной от 32-битной.
В любом случае, обратите внимание, что происходит только одно преобразование. Ваш src_t
преобразуется в dst_t
в al / ax / eax / rax с инструкцией загрузки и сохраняется в dst_t любой ширины. И также остается там как возвращаемое значение.
Нулевая нагрузка является нормальной, даже если вы просто собираетесь прочитать младший байт этого результата.