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

У меня была привычка использовать int для доступа к массивам (особенно для циклов for); однако недавно я обнаружил, что, возможно, я все делал неправильно, и моя система 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.


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

голец
короткая
ИНТ
неподписанный
голец
неподписанный
короткая
неподписанный
int movsbq% sil, % rsi
movswq% si, % rsi
movslq% esi, % rsi

movzbl% sil, % esi

movzwl% si, % esi

MOVL% ESI, % ESI

Таблица нежелательных операций перемещения при
доступ к векторам с "неправильным" типом.

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

Редактировать:

Я думаю, что у меня может быть работоспособная заглушка для исправления 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;

2 ответа

Лязг и гкк есть -Wchar-subscripts, но это только поможет обнаружить char подстрочный тип.

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

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

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

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

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

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

Другие вопросы по тегам