Замена переменных доступа к массивам с правильным целым типом

У меня была привычка использовать int для доступа к массивам (особенно для циклов); однако недавно я обнаружил, что я, возможно, "делаю-все-неправильно", и моя система x86 скрывала от меня правду. Оказывается, int отлично, когда sizeof(size_t) == sizeof(int), но при использовании в системе, где sizeof(size_t) > sizeof(int), он вызывает дополнительную инструкцию mov. size_t и ptrdiff_t кажутся оптимальными для систем, которые я тестировал, не требуя дополнительных mov.

Вот укороченный пример

int vector_get(int *v,int i){ return v[i]; }

    > movslq    %esi, %rsi
    > movl  (%rdi,%rsi,4), %eax
    > ret

int vector_get(int *v,size_t i){ return v[i]; }

    > movl  (%rdi,%rsi,4), %eax
    > ret

ОК, я исправил себя (используя size_t и ptrdiff_t сейчас), теперь как я (надеюсь, не вручную) нахожу эти экземпляры в своем коде, чтобы я мог их исправить?

Недавно я заметил несколько патчей, включая изменения от int до size_t, проходящие через провод, в котором упоминается Clang.


Я собрал таблицу дополнительных инструкций, которые вставлены в каждый экземпляр, чтобы показать результаты "делать-все-неправильно".

         char
        short
             int
unsigned
         char

unsigned
        short

unsigned
            int
movsbq %sil, %rsi
movswq %si, %rsi
movslq %esi, %rsi

movzbl %sil, %esi  


movzwl %si, %esi  


movl %esi, %esi    


Table of unwanted move operations when
accessing vectors with "wrong" type.

Примечание: long, long long, unsigned long, unsigned long long, size_t и ptrdiff_t не требуют дополнительной операции mov * (в основном ничего >= наибольший размер объекта или 8 байтов на 64-битной эталонная система)

Edit:

Я думаю, что у меня может быть работоспособный заглушка для исправления gcc, но я не знаю своего пути по его источнику, чтобы заполнить заглушку и добавить соответствующие бит -Wflag, и, как обычно, самая сложная часть программирования - это именование. -Wunalinged-индекс?

gcc/c/c-typeck.c _______________________________________________

if (!swapped)
    warn_array_subscript_with_type_char (index);
> 
> if ( sizeof(index) < sizeof(size_t) ) 
>   warning_at (loc, OPT_Wunaligned_index,
>       "array index is smaller than size_t");

/* Apply default promotions *after* noticing character types.  */
index = default_conversion (index);

gcc/c-family/c.opt _____________________________________________

trigraphs
C ObjC C++ ObjC++
-trigraphs  Support ISO C trigraphs
> 
> Wunaligned-index
> C ObjC C++ ObjC++
> Warn about array indices smaller than size_t

undef
C ObjC C++ ObjC++ Var(flag_undef)
Do not predefine system-specific and GCC-specific macros

gcc/c-family/c-opts.c __________________________________________

case OPT_Wtrigraphs:
  cpp_opts->warn_trigraphs = value;
  break;
>
> case OPT_Wunaligned_index:
>   cpp_opts->warn_unaligned_index = value;
>

case OPT_Wundef:
  cpp_opts->warn_undef = value;
  break;

Ответы

Ответ 1

clang и gcc имеют -Wchar-subscripts, но это поможет обнаружить только типы индексов char.

Возможно, вы захотите изменить clang или gcc (в зависимости от того, что проще построить на вашей инфраструктуре), чтобы расширить типы, обнаруженные предупреждением -Wchar-subscripts. Если это одношаговое усилие исправить, это может быть самым простым способом сделать это.

В противном случае вам нужно будет найти linter, который жалуется на подписку non size_t/ptrdiff_t; Я не знаю ни одного, у кого есть этот вариант.

Ответ 2

Знак команды movslq расширяет a long (aka 4-байтовое количество) до quad (также 8-байтовое количество). Это связано с тем, что int подписан, поэтому смещение, т.е. -1, равно 0xffffffff как длинное. Если бы вам нужно было просто нулевое расширение (т.е. Не иметь movslq), это будет 0x00000000ffffffff, aka 4294967295, что, вероятно, не то, что вы хотите. Таким образом, компилятор вместо sign-расширяет индекс, чтобы получить 0xffff..., aka -1.

Причина, по которой другие типы не требуют дополнительной операции, состоит в том, что, несмотря на то, что некоторые из них подписаны, они по-прежнему равны 8 байтам. И, благодаря двум дополнениям, 0xffff... можно интерпретировать как либо -1, либо 18446744073709551615, а 64-разрядная сумма будет по-прежнему одинаковой.

Теперь, как правило, если вы должны использовать unsigned int, компилятору обычно нужно будет вставить нуль-расширение вместо этого, чтобы убедиться, что верхняя половина регистра не содержит мусора. Однако на платформе x64 это делается неявно; инструкция, такая как mov %eax,%esi, будет перемещать любое количество из 4 байтов в eax в нижние 4 байта rsi и очистить верхнюю 4, эффективно увеличивая количество. Но, учитывая ваши сообщения, компилятор, похоже, вставляет инструкцию mov %esi,%esi в любом случае, "просто чтобы быть уверенным".

Обратите внимание, однако, что это "автоматическое нулевое расширение" не имеет значения для 1- и 2-байтовых величин - они должны быть вручную увеличены до нуля.