- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
float *quaternions;
for(size_t i = 0; i < 4; i++) {
((int32_t *)&quaternions)[i] = __builtin_bswap32(((int32_t *)&quaternions)[i]);
}
/**GCC: "какая-то магия с приведением и разворотом. Меня всё устраивает, всё ок." **/
А если:
((int32_t *)&quaternions)[0] = __builtin_bswap32(((int32_t *)&quaternions)[0]);
/**GCC: "warning: dereferencing type-punned pointer will break strict-aliasing rules" **/
Dummy00001 11.08.2016 13:37 # +13
это, и различия cast vs conversion в прошлом (пока С и С++ не выровняли) были главными граблями почему С/С++ код ломался если его компилить как C++/C. у меня в одной либе, в С режиме, со стрикт алиасингом, компилятор несколько функций де/сериализации данных в лоб выкидывал: патаму чта поынтеры разных типов, и эти инлайн функции очевидно не имеет ни-ка-ко-го эффекта!!!
Antervis 11.08.2016 14:59 # +12
Dummy00001 11.08.2016 15:25 # +14
pedarok 11.08.2016 16:38 # +2
MiD 11.08.2016 16:44 # +12
Dummy00001 11.08.2016 17:00 # +12
PS моя догадка потому что во втором случае он заменяет `[0]` на обычное `*` - указатель используется напрямую. в первом случае, кастнутый указатель напрямую не используется - он используется как указатель на массив.
bormand 11.08.2016 17:51 # +11
bormand 11.08.2016 17:44 # +12
Так что либо отключай strict aliasing на весь проект, либо вычищай все говнокасты из кода (и молись, что они нигде не затесались).
MiD 11.08.2016 17:50 # +11
bormand 11.08.2016 17:55 # +12
А смысл? Задачка то в принципе неразрешимая, и gcc никогда не найдёт все эти баги...
З.Ы. Походу, единственное решение без UB'ов при включенном strict aliasing выглядит примерно так:
MiD 11.08.2016 17:59 # +11
union {
float *a;
int32_t *b;
} convert;
convert.a = quaternions;
for(size_t i = 0; i < 4; i++) {
convert.b[i] = __builtin_bswap32(convert.b[i]);
}
Dummy00001 11.08.2016 18:06 # +11
bormand 11.08.2016 18:18 # +13
Вроде только так (gcc'шники обещают в примере к опции -fstrict-aliasing, что будет работать):
А с указателями внутри юниона - тот же самый UB.
З.Ы. Причём работа с указателями на поля юниона - тоже UB...
MiD 11.08.2016 18:29 # +11
Antervis 11.08.2016 18:35 # +13
MiD 11.08.2016 18:38 # +12
Dummy00001 11.08.2016 18:34 # +11
не может быть. я вычитывал это на гцц мэйл листах - и в последствии этим пользовался. в моем случае, для конверсии я вместо каста поинтера, передавал указатель на одно поле юниона, а потом читал конвертнутое значение из другого поля юниона.
как я понял, union это официальный способ сказать компилеру о том что данные алиясятся. потому что иначе, если указатели разного типа (исключая char*) то они никогда не алиясятся.
bormand 11.08.2016 18:39 # +11
Ну прочти ман по опции -fstrict-aliasing ;) Я его так поняо, что они гарантируют только алиасинг полей. И только если к ним обращаются через сам union. Т.е. не через перекастованный указатель на юнион, не через указатели на его поля, а только как u.a и u.b.
З.Ы. И твой пример про "передавал указатель, а потом читал из другого" там приведён в качестве примера говнокода :3
Dummy00001 11.08.2016 19:09 # +11
пример из гцц доки:
такого изврата я не делал, но простое `return t.i` работало на ура.
в моем случае, я делал для конверсии локальные юнионы типа `union { int i; char raw[sizeof(int)]; }` что бы типизированые данные читать/писать из потока.
bormand 11.08.2016 19:17 # +11
А чем передача указателя на t.raw в read() отличается от их примера? :)
Dummy00001 11.08.2016 20:33 # +10
Soul_re@ver 11.08.2016 20:34 # +10
LispGovno 12.08.2016 23:38 # +9
Shamill 13.08.2016 06:23 # −34
bormand 11.08.2016 18:10 # +10
Через юнион можно только значения кастовать (если разрабы конкретного компилятора это гарантируют, само собой).
Dummy00001 11.08.2016 18:39 # +10
еще раз, на всякий случай: юнион это официальный способ в C как разраб сообщает компилеру что данные алиясятся. просто в коммерческих компиляторах больше эвристик чем в гцц, почему проблемы на гцц происходят чаще.
ЗЫ я думаю что с указателями на самом деле UB, потому что это всего лишь говорит что значения указателей алиясятся - это не говорит о том что данные на которые они указывают алиясятся. что как по мне было бы не и таким плохим предположением для компилятора.
bormand 11.08.2016 18:41 # +10
Ну майкрософт вон вообще забил на strict aliasing, иначе пришлось бы кучу индусов разогнать...
Soul_re@ver 11.08.2016 18:53 # +11
Soul_re@ver 11.08.2016 18:48 # +10
Легально, но
это UB
Dummy00001 11.08.2016 20:38 # +10
эвристики нужны что бы вместо gcc-шного "UB! сам дурак!!", делать то что подавляющее большинство разрабов ожидает. в конце концов, если ты кучу бабла за навороченый компилер отвалил, а он тебе у виска пальцем крутит... то кастомеры могут и разбежатся.
на самом деле это просто убогие баги компилера. в те времена, с тем конвертором, генерёный асм был конкретным примером этого бага: на пустом месте (где должен был быть код из static inline конвертора) из eax бралось значение (там был мусор от предыдущих операций) и просто писался в вывод. раскажи мне в какой реальности это осмысленая кодогенерация.
Soul_re@ver 11.08.2016 20:53 # +10
Всегда можно написать код, который никакие эвристики не возьмут, а кто-то будет обижаться что код не работает, как хочется. Поэтому нужно делать ровно наоборот.
> кастомеры могут и разбежаться
Поэтому нужно делать постепенно — раз какой-то запутанный код перестал работать, а тебя тыкают носом в стандарт, потом ещё пара строчек сломалась...
Собственно так и происходит: ГЦЦ стал жестче оптимизировать, мелкософтовский компилятор стал жестче оптимизировать, шланг оригинально жестче оптимизировал...
А если какой компилятор забьёт, его зачморят другие: "На этом тесте мы на 30% быстрее другого компилятора, потому что мы не считаем что данные указатели могут указывать на ту же память (что случается в 99.95% случаев)"
Dummy00001 11.08.2016 21:07 # +10
ты пытаешься мне какие-то высокие материи объяснять, в то время как у меня в голове сидит тривиальный код (одна функция в ручную заинлайнена), к которому сводились мои прошлые проблемы:
когда компилер не способен даже такое скомпилировать, то извините меня...
Soul_re@ver 11.08.2016 21:26 # +10
Раньше этот код работал как ожидалось. Но потом решили что лучше бить сразу линейкой по рукам, чем воспитывать говнокодеров и городить костыли каждый раз, когда им кажется что код не работает так как им хочется.
Dummy00001 11.08.2016 21:32 # +9
Soul_re@ver 11.08.2016 21:50 # +9
Учитывая что никто не предлагал ослабить требования к алиасингу, добавив в стандарт случаи, когда компилятору нужно учитывать, что указатели указывают в одно и тоже место, это никому не нужно.
Потому что, если что-то кому-то реально нужно, он берёт и пишет предложение. Может мир, в котором компиляторы начинают следовать стандарту, а не своим несовместимым расширениям, и неправ, но почему-то на это почти никто не жалуется.
guesto 12.08.2016 01:13 # −52
bormand 12.08.2016 17:58 # +10
Но ведь гцц не бьёт линейкой, а вместо этого подкладывает грабли, пока ты отвернулся...
Antervis 11.08.2016 21:13 # +10
guesto 12.08.2016 01:12 # −19
Dummy00001 11.08.2016 18:11 # +9
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
Dummy00001 11.08.2016 17:58 # +9
> курить gcc?
union это официальный способ обхода этой проблемы.
на тему было уже много написано. типа http://stackoverflow.com/questions/2906365/gcc-strict-aliasing-and-casting-through-a-union
pedarok 11.08.2016 16:38 # −30
pedarok 11.08.2016 16:38 # −49
bormand 11.08.2016 18:30 # +9
Soul_re@ver 11.08.2016 18:32 # +10
MiD 11.08.2016 18:32 # +9
bormand 11.08.2016 18:47 # +9
MiD 11.08.2016 19:04 # +10
Antervis 11.08.2016 20:35 # +9
Soul_re@ver 11.08.2016 20:37 # +9
Только динамическая линковка, только хардкор!
Antervis 11.08.2016 21:07 # +9
bormand 11.08.2016 20:37 # +9
Ну или потом кто-нибудь включит глобальную оптимизацию...
Antervis 11.08.2016 21:11 # +9
bormand 12.08.2016 17:58 # +10
MiD 12.08.2016 18:11 # +9
bormand 12.08.2016 18:12 # +10
MiD 12.08.2016 18:19 # +10
А должно быть типа что-то типа :
return *(int)(float*)&a; - само собой это хуита, просто реинтерпреткаст подобного вида, не?
bormand 12.08.2016 18:28 # +11
Конпелятор выкинет эту херню, да и всё... Ведь то место, куда указывает указатель на int не может иметь ничего общего с флоатом...
Soul_re@ver 12.08.2016 18:13 # +9
3_dar 12.08.2016 21:09 # −26
чем это отличается от return (int)a; ?
&a - берем указатель на a (указатель на float)
(float *) - зачем-то приводим float* к float*
* - обратно разименовываем
(int) - приводим float к инту