Научная статья на тему 'Преобразования программ: контроль семантической корректности'

Преобразования программ: контроль семантической корректности Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
179
50
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
статическая семантика / описание семантики / оптимизирующие компиляторы / оптимизирующие преобразования программ / корректность преобразований программ / static semantics / Semantic description / Optimizing compilers / optimizing program transformations / program transformation correctness

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Нис Зиновий Яковлевич

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

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Нис Зиновий Яковлевич

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

The article describes a method for semantics validation checking of optimizing program transformations. The method is based on formal representing of semantic restrictions of the programming languages and ensures that all the possible violations of all the semantics rules of the given language will be detected. The method can be used by developers of optimizing compilers.

Текст научной работы на тему «Преобразования программ: контроль семантической корректности»

УДК 681.3.068

ПРЕОБРАЗОВАНИЯ ПРОГРАММ: КОНТРОЛЬ СЕМАНТИЧЕСКОЙ КОРРЕКТНОСТИ

© 2010 г. З.Я. Нис

Южный федеральный университет, Southern Federal University,

ул. Мильчакова, 8а, г. Ростов н/Д, 344090, Milchakov St., 8a, Rostov-on-Don, 344090,

zzz@rsu.ru zzz@rsu.ru

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

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

The article describes a method for semantics validation checking of optimizing program transformations. The method is based on formal representing of semantic restrictions of the programming languages and ensures that all the possible violations of all the semantics rules of the given language will be detected. The method can be used by developers of optimizing compilers.

Keywords: static semantics, semantic description, optimizing compilers, optimizing program transformations, program transformation correctness.

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

Front-end компилятора гарантирует, что к началу преобразований исходная программа во внутреннем представлении будет семантически корректна. Однако преобразование может перевести даже семантически корректную программу во внутреннем представлении в программу, для которой нарушаются правила статической семантики.

Проверке сохранения функциональной семантики исходной и преобразованной программ посвящены, например, работы [1, 2]. В [1] рассматривается анализ корректности на основе графа информационных зависимостей, в [2] предлагается тестирование программ сравнением трасс их выполнения. В [3, 4] описывается способ тестирования анализаторов статической семантики в компиляторах прогоном компилятора через большое число специально сгенерированных тестовых программ. Качество тестирования напрямую зависит от степени покрытия тестами всех правил. Указанные методы не позволяют явно установить факт нарушения статической семантики, поэтому использовать их для проверки сохранения корректности статической семантики программ преобразованиями нельзя.

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

Работа выполнена в рамках проекта «Открытая распараллеливающая система» (ОРС, [5]).

Постановка задачи: нарушение правил статической семантики преобразованиями

Статическая семантика. Статическая семантика языка программирования - это множество отношений контекстной зависимости конструкций языка и огра-

ничений на эту зависимость (контекстные условия или ограничения).

В качестве примеров отношений контекстных зависимостей можно привести отношения, которые определяют наличие описаний переменных, отношения, вычисляющие типы выражений и т.д.

Типичные примеры контекстных ограничений: идентификатор перед использованием должен быть описан, типы левой и правой частей в операторе присваивания должны быть совместимы и т.д.

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

Пример 1. Раскрутка цикла. Рассмотрим преобразование «раскрутка цикла» [6], заключающееся в замене цикла со счетчиком N-кратным повторением тела цикла, где N - известное во время компиляции программы число итераций в исходном цикле. Преобразование заменит фрагмент: for(int index = 0; index < 3; ++index)

{ loop body(index); }, где loop_body - тело цикла, непосредственно на

фрагмент: {

int index = 0; loop body(index); ++index; loop body(index); ++index; loop body(index); ++index;

}•

Если тело цикла loop_body (...) содержит операторы досрочного перехода к следующей итерации (оператор continue в языке Си), то после применения преобразования эти операторы окажутся вне цикла, что является нарушением семантических ограничений в Си [7].

Если один из операторов тела цикла loop_body (...) имеет метку, то применение преобразования также приведет к некорректной с точки зрения статической семантики программе, так как преобразованный фрагмент будет содержать N одинаковых меток.

Пример 2. Подстановка вперед.

Рассмотрим преобразование «подстановка вперед» [1, 6]. Преобразование подставляет правую часть оператора присваивания вместо всех последующих вхождений левой части этого же оператора в некотором фрагменте. В частном случае, когда правая часть оператора присваивания является константным выражением, это преобразование называется «протягиванием констант» (constant propagation). Преобразование в частности заменяет фрагмент:

x[k] = 5; b = x[k];

на

x[k] = 5; b = 5.

Изменим теперь в исходном фрагменте 2-ю строку

на

b = ++x[k].

После непосредственного применения преобразования получится фрагмент:

x[k] = 5; b = ++5, который противоречит ограничению статической семантики о невозможности применения префиксных инкрементов к выражениям, не являющимся l-value.

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

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

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

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

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

Автоматизация проверок семантической корректности

Графовое представление программ. Распространенным способом представления программ в компиляторах является модель, в которой программа имеет

вид графа (обычно - дерева), узлы которого помечены атрибутами [1, 8, 9].

Корневой вершиной древовидного представления обычно является узел «программа», от которого исходят ветки: «глобальные переменные», «подпрограммы», «пользовательские типы» и т.д. Они в свою очередь содержат поддеревья описания глобальных переменных, операторов, составляющих подпрограммы и т.д. Листовыми вершинами дерева программы обычно являются терминальные символы дерева разбора.

Иногда в качестве графового представления программы используют абстрактные синтаксические деревья (AST, [9]) - деревья синтаксического разбора, из которых удалены узлы, не несущие семантическую информацию, например разделители - запятые, точки с запятой, скобки и т.д.

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

Логический подход к описанию статической семантики. В [10] предложен способ описания статической семантики языков программирования, согласно которому все контекстные зависимости языка задаются совокупностью правил-определений (множеством отношений между синтаксическими объектами программы), а контекстные условия - совокупностью правил-ограничений (ограничений на эти отношения).

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

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

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

Далее в определениях и примерах считаем, что все грамматические символы АеН и BeNB, где Ns и N, -множества нетерминалов грамматики языка, в каждом примере - свои. Под записью symb [attr] понимается значение атрибута attr грамматического символа symb. Во всех случаях считаем, что предикаты P не содержат кванторов и не используют для своего вычисления функций, содержащих кванторы [3, 11].

1. Для любого существует (V 3) V А 3 В | Р (А, В) .

Пример 3.

Правило «для любого оператора continue должен существовать цикл, которому он принадлежит». Здесь AeNcontlnue, BeNloop, Р (А, В) = АеВ (т.е.

предикат истинен тогда и только тогда, когда узел A дерева программы является дочерним для узла В).

2. Для любого, для любого (V V)

V А V В | Pi (А, В) -> Р2 (А, В) .

Пример 4.

Правило «имена всех идентификаторов, объявленных в одной области видимости, должны различаться». В примере следует выбрать:

AeNld, BeNld, Pi (А, В) =

= A[scope] = B[scope],

Р2 (А, В) = A [name] Ф В [пате].

3. Для любого (V) V А Р (А) .

Пример 5.

Правило «выражение в условии любого условного оператора if должно быть приводимо к логическому типу».

Здесь AeNlf, Р (А) = type_compatible (A[condition],boolean), а type compatible -предикат, истинный тогда и только тогда, когда тип первого аргумента совместим с типом, заданным вторым аргументом по присваиванию. Правило может применяться для описания тех ограничений атрибутов объекта, которые не связаны с атрибутами других объектов программы.

4. Существует для любого (3 V) 3 А | V В Р (А, В) .

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

5. Существует (3)3 А Р (А) .

Пример 6.

Это правило - также как и предыдущее - удобно использовать для описания свойств глобальных объектов программы. Например, правило «программа должна содержать точку входа - функцию main». В этом случае AeNfunc, Р (А) = A[name] = "main".

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

Правило нарушено, если истинно не оно, а его отрицание. Если программа изначально семантически корректна (а для встроенных в компилятор преобразований это так: исходную программу уже проверил front-end компилятора), то некорректность может появиться только после применения компилятором преобразования. Следовательно, необходимо определить, какие классы преобразований могут привести к нарушению каких типов правил.

Выведем для каждого из перечисленных типов правил условия их нарушения построением отрицания к соответствующей формуле.

6. Нарушение правила «для любого существует (V 3)».

Отрицание условия выполнения правила V А 3

В | Р (А, В) имеет вид 3 А | V В —,Р (А, В).

Исходное условие будет нарушено, если требуемый узел B не существует хотя бы для одного узла A.

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

7. Нарушение правила «для любого, для любого (V V)».

Отрицание условия выполнения правила

V А V В | Pi (А, В) -> Р2 (А, В) имеет вид 3 А 3 В | Pi (А, В) л —,Р2 (А, В) .

Исходное условие будет нарушено, если найдется хотя бы одна пара узлов A и B такая, что второй предикат вычисляется в ложь, а первый остается истинным.

Таким образом, для данного типа правил «опасными» являются операции создания новых узлов типов Na и NB или изменение атрибутов существующих узлов этих типов.

7. Нарушение правила «для любого (V)».

Отрицание условия выполнения правила V А

Р (А) имеет вид 3 А | -,Р (А) .

Аналогично предыдущему пункту в данном случае опасными являются операции: создания нового узла типа Na; изменения атрибутов существующего узла, используемых предикатом P.

8. Нарушение правила «существует для любого (3 V)».

Отрицание условия выполнения правила 3 А | V В Р (А, В) имеет вид V А 3 В | —,Р (А, В).

Исходное условие нарушится, если узел (узлы) А был удален или были изменены его атрибуты; появились узлы B (вставкой новых или изменением атрибутов существующих) такие, что предикат P не вычисляется в истину.

9. Нарушение правила «существует (3)».

Отрицание условия выполнения правила 3 А |

Р (А) имеет вид V А -,Р (А) .

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

Устранение избыточного диагностирования нарушений. Видно, что каждое элементарное преобразование приводит к нарушению правил сразу нескольких типов. Если применять описанный метод непосредственно, то даже самое несложное преобразование приведет к диагностированию значительного числа потенциальных нарушений статической семантики. При этом большая часть таких нарушений является мнимой, «перестраховочной». Поэтому необходимо предпринимать дополнительные действия по фильтрации списка нарушений.

Литература

1. Allen R., Kennedy K. Optimizing compilers for modern ar-

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

chitectures. San Francisco, USA, 2002. 790 p.

2. Применение модельного подхода для автоматического

тестирования оптимизирующих компиляторов / С.В. Зе-

ленов [и др.]. 2003. URL: http://www.citforum.ru /SE/testing/ compilers (дата обращения: 20.11.2008).

3. Архипова М.В. Генерация тестов для семантических

анализаторов // Вычислительные методы и программирование. 2006. Т. 7. С. 55-70.

4. Штейнберг Б.Я., Напрасникова М.В., Нис З.Я. Тестиро-

вание преобразований Открытой распараллеливающей системы // Искусственный интеллект. 2004. № 3. С. 257-264.

5. Открытая распараллеливающая система. URL: http:

//ops.rsu.ru. 2009 (дата обращения: 20.11.2008).

6. Штейнберг Б.Я. Математические методы распараллели-

вания рекуррентных циклов для суперкомпьютеров с параллельной памятью. Ростов н/Д, 2004. 192 с.

Поступила в редакцию

7. Международный стандарт ISO/IEC 9899:1999 для языка С.

URL: http://www.iso.org/iso/iso_catalogue/catalogue_tc/ cata logue_detail.htm?csnumber=29237 (дата обращения: 25.11.2008).

8. Muchnick S.S. Advanced Compiler-Design and Implementa-

tion. San Francisco, USA, 1997. 860 p.

9. Ахо А., Сети Р., Ульман Д. Компиляторы: принципы,

технологии, инструменты. М., 2003. 768 с.

10. Ильичева О.А. Формальное описание семантики языков

программирования. Электронный учебник. Ростов н/Д, 2007. 223 с.

11. Нис З.Я. Автоматизация проверок семантической кор-

ректности распараллеливающих преобразований // Параллельные вычисления и задачи управления: тр. IV междунар. конф. М., 2006. C. 565-578.

3 февраля 2009 г.

i Надоели баннеры? Вы всегда можете отключить рекламу.