Имеет ли сборка для b
значение %sil
означает b
только 1 байт данных, а оставшиеся 7 байтов имеют только ноль?
Нет, это означает, что *v
(в памяти) только 1 байт.Любые байты после этого не являются частью объекта, на который указывает v
.( Он имеет размер, отличный от b
. )
Если вы предполагаете провести обратный инжиниринг типов a
и b
из asm: обратите внимание, что этоsizeof a
и b
, не sizeof *u
и *v
.Размер операнда инструкций add
соответствует sizeof(*u)
и sizeof(*v)
, а исходные операнды для них являются результатом применения правил целочисленного продвижения / преобразования C к a
и b
.
например l += s
похоже на l += (long)s
, если у нас есть long l; short s;
Если addq
вас смущает, не волнуйтесь, это неверно с байтовым регистром.Попытка собрать это с помощью GAS (gcc -c foo.s
) дает:
foo.s:1: Error: `%sil' not allowed with `addq'
Если мы предположим, что это на самом деле addb %sil, (%rcx)
вместо недопустимого addq
, товопрос ответим.
Если предположить, что операторы C находятся в том же порядке, что и инструкции asm (компилятор решил не изменять их порядок), то это выглядит как код из сигнатуры функции, подобной этой, скомпилированной дляx86-64 System V ABI, поэтому аргументы находятся в RDI, RSI, RDX, RCX в таком порядке.
int f(TYPEA a, TYPEB b, TYPEU *u, TYPEV *v);
TYPEA и TYPEU не того же типа, который мы можемуже скажем, потому что 8> 6, поэтому любой тип qword не подходит, и тот факт, что требуется расширение знака.
Меч a
равен знак - расширен до qword.Так что a
- это 32-разрядное целое число со знаком.В x86-64 System V только int
соответствует этому описанию из основных типов.long
является 64-битным, short
является 16-битным.(В Windows x64 long
также является 32-разрядным типом, но это пахнет как x86-64 System V от выбора регистров.) int32_t
определяется в терминах int
на gcc, если выЯ хочу думать об этом с точки зрения типов фиксированной ширины.
Если бы это было movswq %di, %rdi
, у нас было бы int16_t a
(или short a
).Если бы не было знака расширения, то мы бы знали, что это один из int64_t a
или uint64_t a
.
(*u
- это uint64_t
или int64_t
; мы незнать, какой. (unsigned long long)(int)x;
знак расширяется до ширины unsigned long long
.
Ваша логика 6 = 2 + 4 правильная. Другой тип определенно 16-битный = 2 байта, потому что char
- это 1 байт в системе x86-64, так что размеры sizeof указаны в байтах. И ни один из основных ABI не имеет 5-байтовых целочисленных типов.
short
- это 16-битный тип, как и unsigned short
Мы не можем однозначно определить, какой это.
Мы выводим его только из размера: любой более широкий или меньший целочисленный тип, добавленный к int8_t
, будет усечен доширина. (Знаменитое переполнение здесь может фактически быть неопределенным поведением в C, я забыл. При компиляции для x86-64, результирующий asm ведет себя так, как вы ожидаете, и принимает только младший байт любого целочисленного типа.)
Компиляция с помощью clang 7.0 -O3 (в проводнике компилятора Godbolt) дает almost точно асм, который вы показываете в вопросе (кроме addb
вместо addq
).gcc помещает mov
-mmediate ранее в функцию, что, возможно, позволяет функции декодировать за меньшее количество тактов или, по крайней мере, mov
декодирует цикл раньше вместе с одним из 2-fused-domain-uop memory-инструкции добавления назначения.
typedef int TYPEA;
typedef short TYPEB;
typedef long TYPEU;
typedef char TYPEV;
int f(TYPEA a, TYPEB b, TYPEU *u, TYPEV *v) {
*u +=a;
*v +=b;
return sizeof(a)+ sizeof(b);
}
# clang -O3 output
f: # @f
movslq %edi, %rax # clang uses RAX instead of extending into the same register
addq %rax, (%rdx) # no difference in effect.
addb %sil, (%rcx)
movl $6, %eax
retq
Конечно, unsigned char
или unsigned long
для типов указателей дают то же самое.Или unsigned long long
, который также является 64-битным типом.
Но, что более важно, unsigned short b
также выдаст то же самое asm.