Предположительно, это должно остановить любые нарушения строгого правила псевдонимов, что может возникнуть при вызове кода, который не был полностью очищен, из-за неожиданного поведения при оптимизации (подсказка к этому находится в комментарии выше: «Барьер для оптимизации сглаживания указателя даже через границы функций»).
Строгие правила псевдонимов запрещают разыменование указателей, которые используют псевдонимы для одного и того же значения, когда они имеют несовместимые типы (понятие C, но C++ говорит то же самое с большим количеством слов). Вот небольшая загвоздка: char16_t
и uint16_t
не обязательно должны быть совместимы. uint16_t
на самом деле является опционально поддерживаемым типом (как в C, так и в C++); char16_t
эквивалентен uint_least16_t
, который не обязательно того же типа. Он будет иметь одинаковую ширину на x86, но компилятору не требуется помечать его как на самом деле одно и то же. Это может быть даже намеренно нестрогим с предполагаемыми типами, которые обычно указывают другое намерение, может быть псевдонимом.
В связанном ответе есть более полное объяснение, но в основном это такой код:
uint16_t buffer[] = ...
buffer[0] = u'a';
uint16_t * pc1 = buffer;
char16_t * pc2 = (char16_t *)pc1;
pc2[0] = u'b';
uint16_t c3 = pc1[0];
... если по какой-либо причине компилятор не пометил char16_t
и uint16_t
как совместимые, и вы компилируете с оптимизацией, включающей эквивалент -fstrict-aliasing
, можно предположить, что запись через pc2
не могла изменить что-либо pc1
указывает на и не перезагружать значение, прежде чем присвоить его c3
, вместо этого, возможно, присвоив ему u'a'
.
Код, немного похожий на пример, мог бы появиться в середине процесса преобразования, где предыдущий код успешно использовал uint16_t *
везде, но теперь char16_t *
доступен в верхней части блока для совместимости с ICU 59, перед всем кодом ниже. был полностью изменен, чтобы читать только через правильно типизированный указатель.
Поскольку компиляторы обычно не оптимизируют ручную сборку, наличие блока asm
заставит его проверить все свои предположения о состоянии регистров и других временных значений и выполнить полную перезагрузку каждого значения при первом разыменовании. после U_ALIASING_BARRIER
вне зависимости от флагов оптимизации. Это не защитит вас от каких-либо дальнейших проблем с псевдонимами, если вы продолжите записывать через uint16_t *
ниже преобразования (если вы это сделаете, это законная ваша вина), но это должно, по крайней мере, обеспечить состояние до того, как вызов преобразования не сохраняется таким образом, что впоследствии может быть случайно пропущена запись через новый указатель.
person
Leushenko
schedule
20.09.2017