API с помощью std :: pair перерывы при переключении с C ++ 14 на C ++ 17 на ARM? - PullRequest
1 голос
/ 26 марта 2020

Мы столкнулись с этой очень странной проблемой при обновлении с C ++ 14 до C ++ 17 (Ubuntu 18.04, G CC 7.5.0). Набор инструментов - Linaro для Jetson TX2 и используется по умолчанию.

Справочная информация:

У нас есть приложение C ++ A, которое использует алгоритмы из библиотеки L, также разработанные нами, работающие на Ubuntu 18.04 , Сборки и обширные системные тесты проводились в течение двух лет на Intel и Jetson TX2.

Теперь мы решили перейти на C ++ 17 (-std = c ++ 1z с G CC). Сначала мы собрали L с включенным C ++ 17, и сначала все, казалось, работало нормально, но затем мы заметили, что некоторые тестовые прогоны начали работать странно только на ARM . Как 2 теста из 30, и это было определено c (!).

Затем мы начали исследовать и заметили, что один конструктор в библиотеке, который принял const std::pair<float, float> &, получил поврежденные данные. Внутренний конструктор .first казался .second, а .second всегда был 0. Что-то странное, как это.

Так что это происходит, если A все еще на C ++ 14 и L на C ++ 17.

Ok.

Тогда мы попробовали это наоборот. L на C ++ 14 и приложение A на C ++ 17. Результаты были похожи. Некоторые тесты начали терпеть неудачу (хотя не то же самое), и это было определено c. Причина root снова была той же: каким-то образом std::pair<float, float> в API испортилось.

Таким образом, комбинации до сих пор таковы:

A: C++14, L: C++14, Intel => OK

A: C++14, L: C++17, Intel => OK

A: C++17, L: C++14, Intel => OK

A: C++17, L: C++17, Intel => OK

A: C++14, L: C++14, ARM => OK

A: C++14, L: C++17, ARM => FAIL

A: C++17, L: C++14, ARM => FAIL

A: C++17, L: C++17, ARM => OK

Видимо, это большая коммерческое приложение, поэтому я не могу просто скопировать и вставить код здесь. Сначала я подозревал, что это будет ошибка компилятора (какой она еще может быть), но она кажется слишком очевидной!

И еще:

Мы также недавно заметили, что если мы просто заменим const std::pair<float, float> & просто простыми float аргументами, тесты пройдут снова.

Любые предположения что, черт возьми, происходит? Ошибка компилятора? Как переключение на C ++ 17 может даже в теории вызвать что-то подобное (компилятор точно такой же)? И особенно так (неважно, какой компонент обновляется).

Мы просто не можем найти что-то не так с API. Он почти два года работал без проблем на Intel и ARM с C ++ 14.

EDIT : удалось создать рабочий пример проекта: https://drive.google.com/open?id=1B5SceFB1mKkCnE8iE59Mq0lScK2F0iOl

Инструкции и примеры выходных данных в README.md

Выходы из этого примера на Intel и Jetson TX2:

On Intel (Ubuntu 18.04, GCC 7.5.0) this app prints:

$ ./app/App 
S: 42
L: 3.14
R: 666
In Foo::update(): s: 42
In Foo::update(): l: 3.14
In Foo::update(): r: 666

On Jetson TX2 (Ubuntu 18.04, GCC 7.5.0 / Linaro) this app prints:

$ ./app/App 
S: 42
L: 0
R: 2.39152e+29
In Foo::update(): s: 42
In Foo::update(): l: 0
In Foo::update(): r: 2.39152e+29

Ответы [ 2 ]

4 голосов
/ 27 марта 2020

Как переключение на C ++ 17 может даже в теории вызвать что-то подобное (компилятор точно такой же)?

Есть ЗАГРУЗКИ способов, которыми это могло бы что-то изменить в теории.

Наиболее простым является то, что заголовки стандартной библиотеки имеют много условной компиляции с такими вещами, как :

#if __cplusplus <= 201402L
/* code for C++14 ... */
#else
/* code for C++17 ... */
#endif

Все, что нужно, это несовместимость двух битов кода. Мы очень стараемся, чтобы этого не произошло. Но в теории это может произойти.

Затем мы начали исследовать и заметили, что один конструктор в библиотеке, который принял const std::pair<float, float> &, получил как-то поврежденные данные. Внутренний конструктор .first казался .second, а .second всегда был 0. Что-то странное, подобное этому.

Я не могу воспроизвести что-нибудь подобное. Когда я проверяю сборку, сгенерированную G CC 7.3 для Aarch64 , результаты идентичны для C ++ 14 и C ++ 17 . Поэтому вам нужно будет предоставить больше информации о вашем коде. Не должно быть сложно показать сигнатуру конструктора и данные членов конструктора, без необходимости показывать большие фрагменты проприетарного кода.

Редактировать: Я сократил рабочий пример к этому живому примеру, который показывает, что сгенерированный код для класса с пустой базой отличается для C ++ 14 и C ++ 17, что является ошибкой компилятора: https://godbolt.org/z/E46NFc

Я сообщил как https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94383

4 голосов
/ 26 марта 2020

Я ничего не знаю наверняка, так как я не смотрел, но это похоже на случай изменения двоичного интерфейса. ABI. Это может произойти из-за изменения структуры структуры, возможно, части усилий по объединению пар и кортежей. Это также может быть изменение в правилах заполнения. Или правила выравнивания. Вдруг думаю, что это наиболее вероятный. Если бы он был распределен с использованием выравнивания с плавающей запятой или двойного выравнивания, или одна из сторон решила использовать 64-битное выравнивание для всего, это определенно вызывало бы странные вещи.

Передача по ссылке передает указатель в реализации. Как правило. Так что если структура меняется между версиями C ++, она может иметь разную байтовую разметку.

Это может быть случайностью в компиляторах ARM, потому что, если бы ABI изменялся намеренно, потребовалось бы некоторое усилие, чтобы поместить его в новое пространство имен, как это было сделано для C ++ 11 std :: string в GNU libc ++.

Я бы протестировал некоторые из них, создавая структуры и массивы std :: пар в каждой версии компилятора и выгружая их в дисковые файлы или исследуя их в отладчике. Посмотрите, какие байты меняются.

...