Замена переменных доступа к массивам с правильным целочисленным типом
У меня была привычка использовать 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-байтовым величинам - они должны быть вручную расширены до нуля.