Как программисты гарантируют, что компиляторы создают правильный код?

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

например, в ответе говорится, что:

компилятор Intel 11 делает что-то чудесное. Это взаимозаменяет две петли...

как программист компилятора знает, когда можно обмениваться циклами?

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

как программист компилятора знает, что их компилятор будет генерировать правильный код? Как они проверяют свое заключение? Должны ли они написать набор тестов, который запускает компилятор, и проверяет, что сгенерированный код правильно?

5 ответов


как программист компилятора знает, когда можно обмениваться циклами?

компилятор выполняет серию проверок кода, чтобы определить, безопасно ли обмениваться циклами. Например, если код не полностью встроен, он, вероятно, не сможет обмениваться циклами. Если код изменяет переменную volatile, он не будет обмениваться циклами. Если код хранит значения, вычисленные в предыдущих итерациях цикла, компилятор не будет обмениваться петли. Если они могут быть уверены, что это безопасно, потому что ни одно из этих условий не запускается, компиляция может обмениваться циклами.

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

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

как программист компилятора знает, что их компилятор будет генерировать правильный код?

они делают все, что могут. Иногда они совершают ошибки. Люди отправляют сообщения об ошибках и исправляют их.

как они проверяют свое заключение? Должны ли они написать набор тестов, который запускает компилятор, и проверяет правильность сгенерированного кода?

они абсолютно используют наборы тестов. Когда ошибка обнаружена в GCC, тест специально добавляется в набор тестов, чтобы убедиться, что ошибка исправлена и не повторно введена.


как программист компилятора знает, когда можно обмениваться циклами?

когда модификация не изменяет поведение программы в соответствии со стандартом языка, когда изменение не противоречит самому стандарту.

например, стандарты C и c++ говорят в нескольких местах, что порядок оценки параметров функций и под-выражений не определен. Это предоставляет компилятору свободу генерировать код для их оценки в любом порядке, как сочтет нужным. Если ваша программа зависит от конкретного заказа, она не соответствует стандарту, и вы не имеете права обвинять компилятор в ее "взломе".

компиляторы могут и часто используют анализ кода, логику и математику со всеми этими теоремами для оптимизации кода.

на практике тестирование показывает, правильно ли компилятор выполнил работу.


компиляция-сложный процесс. Программа структурирована как график, и компилятор изо всех сил пытается "оптимизировать" этот график в соответствии с правилами, которые придумали разработчики.

однако нет никакой гарантии, что сгенерированный код близок к "оптимальному". Были проведены исследования так называемых "супер-оптимизаторов", которые пытаются генерировать действительно оптимальный код с помощью автоматизированных механизмов доказательства... т. е. они могут отвечать на такие вопросы, как " есть ли способ скомпилируйте этот алгоритм так, чтобы он занимал меньше X циклов. Денали является одним из таких супер-оптимизаторов, о которых я читал. Для некоторых архитектур этот метод проще, чем для других. Недостатком является то, что эти супер-оптимизаторы может занять часы, если не дни, чтобы составить простую процедуру, которая неприемлема для большинства людей.


один из главных тестов здравомыслия для любого компилятора, написанного на своем языке, - заставить его скомпилировать себя, а затем использовать полученный новый компилятор для компиляции себя снова. Два результирующих компилятора должны быть идентичными, по модулю временных меток.


резюме

хорошо, поэтому я согласен с некоторыми ответами, но я думаю, что люди недооценивают, насколько строгие и консервативные компиляторы в своих преобразованиях и оптимизациях. Короче говоря, хотя оптимальный генерация кода действительно является своего рода черным искусством, и 99% времени эвристика используется, а не доказательства (кто-то говорил о Денали супер оптимизатор) правильное поколение кода, на самом деле, математически звук, и он предназначен для этого. Тот факт, что есть ошибки, из-за огромной сложности сборок компилятора. В качестве бокового узла автоматический доказатель теоремы может иметь ошибки, даже если каждая его часть математически звук, поэтому тот факт, что компиляторы имеют ошибки, не означает, что они являются случайными сборники правил перезаписи.

теперь, чтобы ответить на ваши вопросы по очереди:

как программист компилятора знает, когда можно обмениваться циклами?

существует подполе теории компиляторов под названием Анализ Зависимости это касается таких вопросов. Есть целые - книги по теме, но для вашего конкретного вопроса: компилятор будет автоматически выясните пространство итераций цикла (если границы вычислимы, см. NB внизу), он выяснит зависимости между инструкции циклов (существуют различные виды зависимостей), он будет вычислять расстояние и направление векторы цикла,а затем выяснить, если это законно, чтобы сделать обмен. The Википедия статья об этом просто дает различные типы зависимостей, но имеет ссылки на другие подтемы, а также цитирует книгу, которую я упомянул.

и, в общем, они используют математические доказательства, чтобы продемонстрировать выводы?

нет, а не в целом. Также зависит от того, что мы подразумеваем под доказательством - вы имеете в виду использование доказательства теоремы? The Денали бумага использует SAT решатель, например. The дафний компилятор и язык, Microsoft Research @ Cambridge, выполняет проверку вашего кода в силу аннотаций языка исходного уровня. Компилятор проверяет правильность вашего кода, хотя работать с аннотациями очень сложно (у меня возился с Дафни для проекта в университете, и я говорю по опыту). Кроме того, под "Не вообще" я имею в виду не в gcc, the llvm семья, и я бы подозревал (но я не проверял) в Intel icc. И опять же, мы говорим здесь о компиляции C-like source to machine level, ничего более причудливого.

как программист компилятора знает, что их компилятор будет генерировать правильный код? Как они проверяют свое заключение? Нужно ли им писать набор тестов который запускает компилятор и проверяет правильность сгенерированного кода?

другие ответы охватывали это широко, поэтому больше нечего сказать. Думаю, я могу добавить, что, поскольку компиляторы действительно животные С точки зрения сложности, тестирование их экстенсивно имеет первостепенное значение. Кроме того, я хотел бы повторить, что даже в математически обоснованном фрагменте кода (если мы предположим, что такие понятия формально определены) могут быть ошибки в инфраструктуре, которые обрабатывает этот код, поэтому тестирование всегда важно.

приговором

компиляторы очень сложны. Они должны быть быстрыми, чтобы включить бесшовные циклы разработчика редактирования-компиляции-отладки, а также правильно. Корректность компиляторов кода, который они производят, является своего рода" локально математически обоснованной " - каждое преобразование, которое компилятор делает, должно сохранять семантику исходной программы. Улучшения в скорости управляются heuristical алгоритмы, и такие алгоритмы являются хорошими примерами черная магия методы, в том смысле, что они не дают никаких доказательств того, что полученный код будет быстрее. Они также не дают никаких формальных доказательств того, что это будет правильно, но каждое преобразование предназначено для создания правильного семантического кода сохранения. Наконец, самая горячая тенденция в компиляторах (я думаю, как и везде в информатике в наши дни) - использовать machine learning для оптимизации, поэтому у нас будет еще больше ручек для настройки :) ура!

NB

почти все в компиляторе "back end", т. е. codegenerator, является либо NP полным, либо неразрешимым. В частности, определить, является ли обмен циклами законным, невозможно, поскольку границы цикла могут быть произвольными выражениями (скажем, вызовами функций). Однако, если границы вычислимы (скажем, целые числа или что-то простое), то именно здесь начинается анализ зависимости.