Язык программирования Т++



Язык программирования T++ является синтаксически и семантически гладким языковым расширением стандартного языка программирования C++. Под синтаксической и семантической гладкостью здесь понимается прежде всего наличие естественного вложения конструкций языка C++ в расширенный (по отношению к нему) синтаксис и семантику языка T++. Естественность вложения означает использование уже существующих синтаксических

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

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

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


В качестве среды исполнения программ на языке T++ в настоящее время используется

Т-система с открытой архитектурой (OpenTS). Описание базовых сущностей OpenTS дается в отдельном приложении.


При создании языка T++ преследовались следующие цели:


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

    расширением языка C++, обеспечивающим решение поставленной задачи;

  2. Языковое расширение должно быть прозрачно по отношению к языку C++,

    то есть должна быть обеспечена возможность компиляции и запуска программы на языке T++ при помощи стандартного С++-компилятора и стандартной среды исполнения

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

  3. В языке должны быть оставлены возможности для отражения дополнительных

    (нестандартных) атрибутов, которые бы определялись спецификой реализации языка.

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

    возможность для введения специфических конструкций, зависящих от реализации.

Краткое описание языка T++


Поскольку язык программирования T++ является расширением языка С++, то мы

перечислим новые добавленные в язык С++ конструкции; контексты, в которых возможно

их употребление, и то, как они влияют на выполнение итоговой (то есть откомпилированной

с помощью соответствующего компилятора и запущенного в соответствующей среде исполнения) программы.


Краткое описание синтаксиса


Описание синтаксиса дадим как перечисление новых ключевых слов и контекстов,

в которых возможно их употребление


Всего к стандартному набору С++ добавляется пять ключевых слов: tfun, tval, tptr, tout, tct

и две новых стандартных функции: tdrop, twait


Первые четыре ключевых слова tfun, tval, tptr, tout являются по сути синонимами для

более общей конструкции tct (определение Т-контекста) с определенными параметрами.


То есть можно сказать, что первична только одна конструкция определяющая --

Т-контекст в данной точке C++-программы. На основании этого Т-контекста

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


Две новых стандартных функции twait и tdrop необходимы для выполнения специфических

операций, у которых нет прямых аналогов в языке C++. Их также можно трактовать как

модификаторы Т-контекста в коде T++-программы, определяемые в форме вызова функции на этапе исполнения программы.


Ниже смысл и контексты применения каждого ключевого слова рассмотрены подробнее.


tfun - атрибут, который можно указать непосредственно перед описанием типа функции.

Функция не может являться методом; это должна быть обычная top-level функция.

Описанная с помощью этого ключевого слова функция кратко называется Т-функцией.


tval – атрибут, который можно указать непосредственно перед описанием типа переменной.

Описанная с помощью этого ключевого слова переменная кратко называется Т-переменной;

В качестве значения Т-переменная содержит неготовую величину (Т-величину)


tptr – T++-аналог для определения указателей (ссылок). Используется для описания глобальных указателей в структурах данных. Описанный с помощью этого ключевого слова указатель кратко называется Т-указателем.


tout - атрибут, который можно указать непосредственно перед описанием типа выходного

аргумента Т-функции. T++-аналог для определения аргументов, передаваемых по ссылке

('&') для их дальнейшей модификации в теле функции.


tct – явное определение T-контекста. Служит для определения дополнительных свойств

Т-сущностей (специфических сущностей, поддерживаемой Т-системой).

В любом случае синтаксически описание Т-контекста содержит не более двух конструкций расширенного синтаксиса: одну из основных конструкций (tfun, tval, tptr, tout) и дополнительную конструкцию tct((...)), где в скобках указываются параметры изменения

Т-контекста, конкретные значения и смысл которых определяются реализаций языка.


tdrop – стандартная функция от одного аргумента. Может быть вызвана от любой
Т-величины.


twait - стандартная функция от двух аргументов: Т-сущности и паттерна событий.

Возвращает статус произошедших с Т-сущностью соответствующих указанному паттерну событий.


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

Краткое описание семантики


Семантика Т-переменных


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


Т-переменные совместимы по большинству операций с обычными переменными;

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

с помощью оператора & (указатель на Т-переменную является Т-указателем).


Связь с неготовой величиной разрывается в момент уничтожения Т-переменной

(например, при выходе из блока, в котором Т-переменная была объявлена),

а также при вызове функции tdrop. При этом Т-переменная связывается с новой,

только что созданной неготовой величиной.


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



Семантика Т-функций


Т-функции являются функциями, которые выполняются каждая в своем потоке

управления (треде). При этом они могут одновременно выполняться на разных

процессорах в многопроцессорной системе.


Т-функции взаимодействуют между собой при помощи Т-переменных, которые

содержат Т-величины. Семантика Т-переменных обеспечивает необходимую

синхронизацию и семантическую совместимость , которую может ожидать программист

от аналогичной C++-программы. Поддержка семантики Т-функций и Т-переменных обеспечивается соответствующей средой исполнения T++-программ.


При вызове Т-функции ей в качестве входных аргументов передаются те данные, которые

содержались в точке вызова; дальнейшие изменении Т-переменных вызывающей

Т-функцией допускаются произвольное число раз и не приводят к видимому

эффекту для вызванной Т-функции.


В качестве выходных аргументов (описываемых при помощи атрибута tout) указываются

собственные Т-величины, при этом они становятся неготовыми, а их поставщиком

является вызванная Т-функция. Значением Т-функций также является неготовая величина,

которая может быть присвоена (в точке вызова) любой собственной Т-величине.


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


Семантика Т-указателей


Т-указатели являются аналогами C-указателей для неготовых величин.

Т-указатели совместимы по большинству операций с обычными указателями;

их можно присваивать таким же образом, обращаться за их значением, обращаться

к значению помощью операторов -> и * (результатом применения оператора * является

неготовая величина).



Семантика дополнительных функций tdrop и twait


Дополнительные функции tdrop и twait позволяют совершать специфические по отношению

к языку C++ операции: разрыв связи с неготовой величиной и ожидание определенных

событий (как правило ожидание готовности одной или нескольких Т-величин)


При вызове функции tdrop Т-функцией-поставщиком Т-значения неготовая величина

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

При этом возникает соответствующее событие, которое влечет за собой продолжение

исполнения всех приостановленных по причине ожидания потоков.


При вызове функции tdrop Т-функцией-потребителем Т-значения ссылка на неготовую

величину теряется и счетчик ссылок на неготовой величине уменьшается. Обнуление

счетчика ссылок на неготовой величине влечет за собой инициацию процесса остановки

Т-функции-поставщика Т-значения, если только Т-функции-поставщик не производит

других значений для кем-либо ожидаемых неготовых величин.


При вызове функции twait указывается Т-сущность (обычно Т-величина или массив

Т-величин) и паттерн для ожидаемых событий. Возвращается статус событий.

Конкретные паттерны могут определяться реализацией языка T++; от реализации

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

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


Пример программы на языке T++


Ниже приведен пример простой программы, которая может быть

откомпилирована и выполнена с помощью соответствующих

программных средств для языка T++.


Программа вычисляет N-ную строку треугольника Паскаля,

используя при этом рекурсию (что порождает большое количество

одновременно работающих функций и является хорошим тестом

для системы динамического распараллеливания)


Специфические ключевые слова языка T++ выделены.

Как видно из программы, их количество весьма незначительно;

они потребовались лишь для указания, что функцию GetNumber

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

описания массива неготовых значений values.




#include <stdio.h>


tfun int GetNumber( int numberOfRow, int position ) {

if ( position == 1 || position == numberOfRow ) return 1;

else return GetNumber( numberOfRow - 1, position - 1 ) +

GetNumber( numberOfRow - 1, position );

}


tfun int main( int argc, char** argv ) {

if ( argc != 2 ) {

printf( "Usage: pascal <numberOfRow>\n" );

return 1;

}

int n = atoi( argv [1] );

tval int values [ n ];

for ( int i = 0; i < n; i++ )

values [i] = GetNumber( n, i + 1 );


for ( int i = 0; i < n; i++ )

printf( "%d ", (int) values [i] );

printf( "\n" );

return 0;

}



Требования к компилятору


К компиляторам с языка T++ предъявляются следующие требования:


  1. Режим отладки (полная отладочная информация + соответствующая библиотека с поддержкой отладки). В этом режиме должна быть обеспечена быстрая компиляция (оптимизация не требуется) и максимально возможная поддержка для отладки.

  2. Режим обычного запуска (оптимизация кода + поддержка диагностики). В этом режиме осуществляется большинство запусков на этапе опытной эксплуатации приложения и возможна частичная поддержка отладки и диагностики при возникновении различных исключительных ситуаций.

  3. Режим максимальной оптимизации (максимально возможная оптимизация + отсутствие санитарных проверок на этапе выполнения). Используется для хорошо отлаженных приложений. Должна выполняться максимально возможная оптимизация и из кода должны удаляться все диагностические проверки, оказывающие сколь либо заметное влияние на быстродействие программы.



Конвенции компиляции T++ -> C++ (для среды исполнения OpenTS)


Конвенции компиляции описывают правила преобразования исходной программы на языке T++ в эквивалентную ей программу на языке C++, использующую сущности, определенные в заголовочном файле Т-рантайма trt.


Описываемые ниже конвенции предполагают, что исходная программа уже была обработана стандартным препроцесcором языка C++ (то есть что все макроопределения уже раскрыты и текст исходной программы синтаксически удовлетворяет грамматике языка C++).


Мы дадим описание преобразований с использованием макроподстановок: если в описании преобразования указывается макроопределение (по выбранной конвенции – прописными буквами), то это означает, что в результирующем тексте нужно дополнительно сделать макроподстановки в соответствии с указанными макроопределениями (определены в заголовочном файле txx).


Компиляция определений Т-функций


Определения (прототипы) Т-функций обнаруживаются в теле исходной программы и первое из них заменяется на следующее выражение (все последующие заключаются в комментарии или удаляются):


TFUNDEF(type,fun,pars,stas,args,argi,outs,outi,msize)


,где


Примечание:

В случае анонимных параметров компилятор должен самостоятельно сгенерировать

их имена; например для прототипа tfun int f (int *, char **) можно сгенерировать имена

tfun int f (int* arg1, char** arg2).


Компиляция реализации (программного кода) Т-функций


Заголовки реализаций Т-функций обнаруживаются в теле исходной программы и заменяются на следующее выражение:


TFUNIMPL(type,fun,pars,stas,args,argi,outs,outi,msize) {

<function implementation code>

}


,где


Примечание:

В случае отсутствия ранее объявленного прототипа перед компиляцией реализации

Т-функции должно имитироваться наличие прототипа: перед компиляцией кода следует сгенерировать и откомпилировать соответствующий прототип.



Компиляция объявления Т-переменных


Объявления Т-переменных (конструкции вида tval type var1 [,var2]...; ) должны преобразовываться в операторы объявления Т-переменных Т-суперструктуры

(конструкции вида TVar<type> var1 [,var2]...;)



Компиляция объявления Т-указателей


Объявления Т-указателей (конструкции вида type tptr ptr1 [,tptr ptr2...] [,var3]...; ) должны преобразовываться в операторы объявления Т-указателей Т-суперструктуры

(конструкции вида TPtr<type> ptr1 [, ptr2...]; [TVar<type> var3]...;)


...



Исследованные технологии для построения компиляторов



Для построения компиляторов языка T++ в настоящее время используется две технологии.

Первая основана на технологии конвертирования языковых расширений C++ с помощью

технологии OpenC++, вторая использует инфраструктуру компилятора GCC, в который

добавляется языковой фронтенд для языка T++.


К достоинствам первой технологии можно отнести относительную простоту, возможность

контролировать ход трансляции рассматривая листинг отконвертированной

T++-программы. При этом также возможно применение различных компиляторов

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


Достоинствами второй технологии является прямая интеграция с компилятором GCC:

после обработки специфических конструкций языка T++ итоговая программная структура

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


В обоих случаях программа к некоторый момент времени преобразуется в семантически эквивалентную ей C++-программу, в которую добавлены соответствующие операторы и определения из заголовочного файла txx (конвенции компиляции для языка T++) и

trt (T-runtime). Дальнейшее обеспечение семантики конструкций языка T++ обеспечивается средой исполнения (библиотека libtrt).