во время выполнения procprob, может ли b быть любым типом данных? - PullRequest
0 голосов
/ 27 октября 2018

код c:

 *u +=a;
 *v +=b;
 return sizeof(a)+ sizeof(b);

x86-64 код:

movslq %edi, %rdi
addq %rdi, (%rdx)
addb %sil, (%rcx)
movl $6 %eax
ret

Я знаю, что movl $ 6% eax означает 2 + 4 (или 4 + 2), и один - int, а другой - короткий.

Но когда мы думаем, что игнорируя movl $ 6% eax, b может быть любым типом данных, таким как 1, 2, 4 и 8 байтов типа данных. У меня есть вопрос по этому поводу.

  1. Предположим, что b длинный ( Конечно, мы игнорируем movl $ 6% eax ). Является ли сборка для b, представляющая собой% sil, означает, что b имеет только 1-байтовые данные, а оставшиеся 7 байт имеют только ноль? Приведите несколько примеров, когда b может иметь регистр% sil (1-байтовый регистр), даже если b имеет тип данных long (регистр 8 байтов)

1 Ответ

0 голосов
/ 28 октября 2018

Имеет ли сборка для 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.

...