Comments 21
Спасибо за статью. Вот только написать
Пусть мы решили написать функцию, которая меняет знак у float, меняя 31-й бит бинарного представления floatи не сказать о том, что так лучше не делать — может быть чревато.
+4
Если не вдаваться в ккаие-то теоретические дебри стандарта и чисто гипотетические случаи, то это ничем не чревато. Если используется стандарт IEEE754, то эта функция изменит знак числа. Я проверил генерацию кода на gcc и clang для основных платформ (x86 и ARM) и всё компилируется правильно.
Если вы про gcc4.4.7 -m32 -strict-aliasing, то эта проблема проявляется исключительно в этой конкретной версии компилятора, и нигде более. Вот тут я немного написал про это: 32bit-me.livejournal.com/174869.html
Если вы про gcc4.4.7 -m32 -strict-aliasing, то эта проблема проявляется исключительно в этой конкретной версии компилятора, и нигде более. Вот тут я немного написал про это: 32bit-me.livejournal.com/174869.html
+3
Причем здесь проявляется или не проявляется? Вам ли не понимать, к чему может привести UB в коде. Если флаг -strict-aliasing указан то все, приехали.
+6
Но его можно не указывать!
На самом деле, для микроконтроллеров и других устройств без fpu операции манипулирования битами могут сильно оптимизировать скорость исполнения, и отказываться от них не следует. Подобные ограничения могут сильно усложнить дело.
На самом деле, для микроконтроллеров и других устройств без fpu операции манипулирования битами могут сильно оптимизировать скорость исполнения, и отказываться от них не следует. Подобные ограничения могут сильно усложнить дело.
+2
Я не предлагаю отказываться полностью. Я предлагаю лишь не замалчивать потенциальные проблемы.
Мир контроллеров это особый мир, со своими правилами и проблемами, и к нему уж точно не стоит апеллировать в статье на общую тему.
Те, кому надо выжать из камня максимум, рано или поздно сами придут к битовым операциям, но уже с полным осознанием последствий.
Мир контроллеров это особый мир, со своими правилами и проблемами, и к нему уж точно не стоит апеллировать в статье на общую тему.
Те, кому надо выжать из камня максимум, рано или поздно сами придут к битовым операциям, но уже с полным осознанием последствий.
+4
>> Но его можно не указывать!
Есть стандартный способ кастить float в его бинарное представление через union. И не надо отключать strict aliasing.
union { int dst; float src; } x;
Есть стандартный способ кастить float в его бинарное представление через union. И не надо отключать strict aliasing.
union { int dst; float src; } x;
-2
Это гарантируется только и исключительно в GCC — да и там со множеством оговорок.
memcpy
— наше всё!+2
«Множество оговорок» про доступ к элементам union через указатели так себе аргумент. Ведь union и используется для того чтобы избежать использования указателей.
У меня для кастов есть темплейтная функция и проблем с ней не замечено на различных компиляторах и платформах.
У меня для кастов есть темплейтная функция и проблем с ней не замечено на различных компиляторах и платформах.
-2
Ведь union и используется для того чтобы избежать использования указателей.Union — это средство экономии памяти. Точка. Запись в одно поле union'а, а чтение из другого — это неопределённое поведение со всеми вытекащими (вызовом невызываемой фукнции и форматированием винчестера).
GCC даёт некоторые, очень ограниченные гарантии, что программа, которая такое делает не сломается сразу же.
Вот так:
union a_union {
int i;
double d;
};
int f() {
union a_union t;
t.d = 3.0;
return t.i;
}
работает. Вот так:int f() {
double d = 3.0;
return ((union a_union *) &d)->i;
}
уже нет.Clang не делает даже этого. Единственный гарантированно работающий способ — это bit_cast (ну или ручная реализация оного через
char*
).Дискусия на тему как бы это можно было разрешить делать в C++20 без
memcpy
— всё ещё продолжается.У меня для кастов есть темплейтная функция и проблем с ней не замечено на различных компиляторах и платформах.Вам очень хочется дождаться того, чтобы у заказчика отформатировался случайно винчестер? Дождётесь.
Мы говорили с разработчиками clang'а по поводу этого расширенного ипользования union'ов, разрешённого GCC. Их ответ был «ну, мы, по возможности, стараемся не ломать такой код, но если не уследим — поводом срочно выпускать фикс это не будет».
Потому и в документации ничего нет.
+6
Это гарантируется стандартами C99 и C11. так что для C-кода всё норм, достаточно затребовать поддержку стандарта.
0
Вы «затребовали поддержку стандарта», разработчики компиляторов сказали нет, нет и не будет. Ваш следующий шаг? Нанять других разработчиков и сделать другой компилятор?
Если вы работаете с указателями и компилятор «не видит» вашего union'а — то он будет нарушать эту часть стандарта — можете убедиться. А если вы хотите скопировать данные в
Что делать с type punning'ом через union'ы в стандарте C (где он таки действительно определён) — сейчас обсуждается (скорее всего в следующей версии либо сильно обкорнают, либо выкинут), но на практике компиляторы его уже не поддерживают.
Если вы работаете с указателями и компилятор «не видит» вашего union'а — то он будет нарушать эту часть стандарта — можете убедиться. А если вы хотите скопировать данные в
union
, дальше использовать его для преобразования типов, потом скопировать обратно (что работает в GCC и обычно работает в clang
) — так проще уж использовать memcpy
и построенный поверх него bitcast.Что делать с type punning'ом через union'ы в стандарте C (где он таки действительно определён) — сейчас обсуждается (скорее всего в следующей версии либо сильно обкорнают, либо выкинут), но на практике компиляторы его уже не поддерживают.
+1
Я просто не пойму зачем провоцировать людей на подобное трюкачество? Вот такая функция точно так же будет преобразована в одну инструкцию fneg на тех компиляторах, которые это умеют:
Только, в отличие от примера в статье, данная функция:
float negate(float num) {
return num * -1;
}
Только, в отличие от примера в статье, данная функция:
- Абсолютно понятна читающему
- Ни при каких условиях не провоцирует неопределенное поведение
- Не работает с указателями — больше простора для оптимизации
- На кривых компиляторах все равно отработает как надо
+6
В данном случае проще написать
Основные компиляторы для основных платформ с этим справятся, это действительно так. Но в целом, есть случаи, когда нам необходимо сделать преобразование float в его битовое придставление, и работать непосредственно с ним.
Код приведен просто для примера, чтобы показать, что компилятор на самом деле довольно умный.
return -num;
Основные компиляторы для основных платформ с этим справятся, это действительно так. Но в целом, есть случаи, когда нам необходимо сделать преобразование float в его битовое придставление, и работать непосредственно с ним.
Код приведен просто для примера, чтобы показать, что компилятор на самом деле довольно умный.
+6
Список оптимизаций для впечатлительных
-targetlibinfo
-tti
-tbaa
-scoped-noalias
-assumption-cache-tracker
-profile-summary-info
-forceattrs
-inferattrs
-ipsccp
-globalopt
-domtree
-mem2reg
-deadargelim
-domtree
-basicaa
-aa
-instcombine
-simplifycfg
-basiccg
-globals-aa
-prune-eh
-always-inline
-functionattrs
-domtree
-sroa
-basicaa
-aa
-memoryssa
-early-cse-memssa
-speculative-execution
-domtree
-basicaa
-aa
-lazy-value-info
-jump-threading
-lazy-value-info
-correlated-propagation
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-libcalls-shrinkwrap
-loops
-branch-prob
-block-freq
-pgo-memop-opt
-domtree
-basicaa
-aa
-tailcallelim
-simplifycfg
-reassociate
-domtree
-loops
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-loop-rotate
-licm
-loop-unswitch
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-loops
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-indvars
-loop-idiom
-loop-deletion
-loop-unroll
-memdep
-memcpyopt
-sccp
-domtree
-demanded-bits
-bdce
-basicaa
-aa
-instcombine
-lazy-value-info
-jump-threading
-lazy-value-info
-correlated-propagation
-domtree
-basicaa
-aa
-memdep
-dse
-loops
-loop-simplify
-lcssa-verification
-lcssa
-aa
-scalar-evolution
-licm
-postdomtree
-adce
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-barrier
-basiccg
-rpo-functionattrs
-globals-aa
-float2int
-domtree
-loops
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-loop-rotate
-loop-accesses
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-loop-distribute
-branch-prob
-block-freq
-scalar-evolution
-basicaa
-aa
-loop-accesses
-demanded-bits
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-loop-vectorize
-loop-simplify
-scalar-evolution
-aa
-loop-accesses
-loop-load-elim
-basicaa
-aa
-instcombine
-latesimplifycfg
-domtree
-basicaa
-aa
-instcombine
-loops
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-loop-unroll
-instcombine
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-licm
-alignment-from-assumptions
-strip-dead-prototypes
-domtree
-loops
-branch-prob
-block-freq
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-branch-prob
-block-freq
-loop-sink
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-instsimplify
-simplifycfg
-verify
-tti
-tbaa
-scoped-noalias
-assumption-cache-tracker
-profile-summary-info
-forceattrs
-inferattrs
-ipsccp
-globalopt
-domtree
-mem2reg
-deadargelim
-domtree
-basicaa
-aa
-instcombine
-simplifycfg
-basiccg
-globals-aa
-prune-eh
-always-inline
-functionattrs
-domtree
-sroa
-basicaa
-aa
-memoryssa
-early-cse-memssa
-speculative-execution
-domtree
-basicaa
-aa
-lazy-value-info
-jump-threading
-lazy-value-info
-correlated-propagation
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-libcalls-shrinkwrap
-loops
-branch-prob
-block-freq
-pgo-memop-opt
-domtree
-basicaa
-aa
-tailcallelim
-simplifycfg
-reassociate
-domtree
-loops
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-loop-rotate
-licm
-loop-unswitch
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-loops
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-indvars
-loop-idiom
-loop-deletion
-loop-unroll
-memdep
-memcpyopt
-sccp
-domtree
-demanded-bits
-bdce
-basicaa
-aa
-instcombine
-lazy-value-info
-jump-threading
-lazy-value-info
-correlated-propagation
-domtree
-basicaa
-aa
-memdep
-dse
-loops
-loop-simplify
-lcssa-verification
-lcssa
-aa
-scalar-evolution
-licm
-postdomtree
-adce
-simplifycfg
-domtree
-basicaa
-aa
-instcombine
-barrier
-basiccg
-rpo-functionattrs
-globals-aa
-float2int
-domtree
-loops
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-loop-rotate
-loop-accesses
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-loop-distribute
-branch-prob
-block-freq
-scalar-evolution
-basicaa
-aa
-loop-accesses
-demanded-bits
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-loop-vectorize
-loop-simplify
-scalar-evolution
-aa
-loop-accesses
-loop-load-elim
-basicaa
-aa
-instcombine
-latesimplifycfg
-domtree
-basicaa
-aa
-instcombine
-loops
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-loop-unroll
-instcombine
-loop-simplify
-lcssa-verification
-lcssa
-scalar-evolution
-licm
-alignment-from-assumptions
-strip-dead-prototypes
-domtree
-loops
-branch-prob
-block-freq
-loop-simplify
-lcssa-verification
-lcssa
-basicaa
-aa
-scalar-evolution
-branch-prob
-block-freq
-loop-sink
-lazy-branch-prob
-lazy-block-freq
-opt-remark-emitter
-instsimplify
-simplifycfg
-verify
+1
UFO just landed and posted this here
Отключая проходы один за другим, находим искомый
Попробуйте также: https://llvm.org/docs/OptBisect.html
+1
Не понял, почему автор считает, что неинициализированный указатель должен быть равен null?
0
Sign up to leave a comment.
Почему LLVM может вызвать никогда не вызываемую функцию?