Ada_Ru форум

Обсуждение языка Ада

GNAT & LLVM

Оставить новое сообщение

Сообщения

Alexey Veselovsky
GNAT & LLVM
2009-03-27 09:20:37

Кто-нибудь пробовал собирать GNAT в качестве фронтенда для LLVM?

Вообще, насколько это перспективно?

On Fri, Mar 27, 2009 at 12:20:37PM +0300, Alexey Veselovsky wrote:

Кто-нибудь пробовал собирать GNAT в качестве фронтенда для LLVM?

 

Да. "LLVM--Low Level Virtual Machine--and Ada"

http://groups.google.com/group/comp.lang.ada/browse_thread/thread/b1f4420d01b2c4eb/

 

Вообще, насколько это перспективно?

 

К сожалению LLVM ориентирован на Си подобные языки, не имеет многих фишек, нужных чотбы Ада работала красиво:

* Контроль переполнение целых

* Вложенные подпрограммы

* Исключения

* Out-аргументы

* гибкий стек

 

В результате, хотя и можно компиляьт Аду в LLVM, но работать оно будет хуже чем Си, для которой LLVM заточена.

 

Мне больше по душе TenDRA, жаль только она не развивается совсем.

--

Maxim Reznik

Вообще, насколько это перспективно?

К сожалению LLVM ориентирован на Си подобные языки, не имеет многих

Это какие например? Си штука низкоуровневая. Остальные языки суть высокоуровневы.

 

фишек, нужных чотбы Ада работала красиво:

* Контроль переполнение целых

Реализуется на llvm также как и на железяке реальной.

 

* Вложенные подпрограммы

Аналогично.

 

* Исключения

Аналогично. Причем исключения есть сейчас чуть ли не везде, кроме С.

 

* Out-аргументы

А что в них такого необычного?

 

* гибкий стек

Не менее гибкий нежели на реальной железяке. alloca имеет место быть.

 

В результате, хотя и можно компиляьт Аду в LLVM, но работать оно будет

хуже чем Си, для которой LLVM заточена.

Гм. Почему же таки хуже то? Ада на реальной железке работает хуже чем Си?

On Fri, Mar 27, 2009 at 04:38:46PM +0300, Alexey Veselovsky wrote:

Вообще, насколько это перспективно?

К сожалению LLVM ориентирован на Си подобные языки, не имеет многих

Это какие например? Си штука низкоуровневая. Остальные языки суть высокоуровневы.

 

 

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

 

фишек, нужных чотбы Ада работала красиво:

* Контроль переполнение целых

Реализуется на llvm также как и на железяке реальной.

 

Ada: A + B

Asm: mov ax, A ; схавать А

add B ; схавать B

into ; прерывание если переполнение

 

LLVM: Tmp : int64 := int64(A) + int64(B);

if Tmp > int32'last then raise Constraint_Error; end if;

 

Не думаю, что это "как на железяке".

 

* Вложенные подпрограммы

Аналогично.

Некторорые ЦПУ имеют специальные механизмы ускоряющие доступ

к переменным во вложенных процедурах, например, дисплеи.

Даже Intel имеет инструкции enter/leave.

 

Согласен, можно построить цепочку указателей на фреймы,

передавать указатель в процедуру дополнительным параметром

и это подойдет для LLVM. Но будет ли это оптимальным на

Интел? Сомнительно. Оптимальным на всех ЦПУ на которых

есть/будет LLVM, еще сомнительней.

 

 

* Исключения

Аналогично. Причем исключения есть сейчас чуть ли не везде, кроме С.

Они даже в Си есть! Видел набор макросов, использующих setjmp/longjmp. Но вот даже gnat уже умеет zero cost exceptions, а в LLVM такого

сделать нелья, как мне видиться.

 

* Out-аргументы

А что в них такого необычного?

 

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

 

 

* гибкий стек

Не менее гибкий нежели на реальной железяке. alloca имеет место быть.

 

Как насчет такого?

function Spaces (X: Natural) return String is

begin

return (1 .. X => 'z');

end;

 

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

 

 

В результате, хотя и можно компиляьт Аду в LLVM, но работать оно будет хуже чем Си, для которой LLVM заточена.

Гм. Почему же таки хуже то? Ада на реальной железке работает хуже чем Си?

Аде нужны средства которыми обладают ЦПУ (язык не поворачивается

добавить слово современные), но которых нет в LLVM, потому, что

LLVM заточена на скудный Си, которому +,-,*,/ и больше ничего не надо.

TenDRA делали с многих языков, включая Ада. Создатели LLVM об Аде наверное ничего не знают, им было достаточно Си, чтобы определиться с набором поддерживаемых операций. Что тут еще сказать???

 

PS LibJIT побольше умеет для Ады чем LLVM

http://ru.wikipedia.org/wiki/DotGNU#libjit

Возможно будет интересно кому-то.

 

--

Maxim Reznik

Maxim Reznik пишет:

 

* Out-аргументы

А что в них такого необычного?

 

В том что они передаются по копированию, а не по ссылке, вернувшись

из процедуры - забери значение со стека. Этого на Си не сделать, тк

стек чиститься в самой процедуре.

 

Значит, память должен выделять вызывающий. Размер out–параметров при вызове известен, в отличие от размера результата.

 

* гибкий стек

Не менее гибкий нежели на реальной железяке. alloca имеет место быть.

 

Как насчет такого?

function Spaces (X: Natural) return String is

begin

return (1 .. X => 'z');

end;

 

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

ее результатом, вернулась, не почистив стек. На "реальной железяке"

такое делается влет, на TenDRA тоже, на LLVM нет.

 

В GNAT используется вторичный стек. Указатель вторичного стека хранится в TLS. Это в LLVM сделать можно.

 

-- If you want to get to the top, you have to start at the bottom

On Sat, 28 Mar 2009 09:08:13 +0600, you wrote:

 

Maxim Reznik пишет:

 

Как насчет такого?

function Spaces (X: Natural) return String is

begin

return (1 .. X => 'z');

end;

 

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

 

В GNAT используется вторичный стек. Указатель вторичного стека хранится в TLS. Это в LLVM сделать можно.

 

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

 

--

Regards,

Dmitry A. Kazakov

http://www.dmitry-kazakov.de

On Sat, Mar 28, 2009 at 09:08:13AM +0600, Иван Левашев wrote:

Maxim Reznik пишет:

 

* Out-аргументы

А что в них такого необычного?

 

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

 

Значит, память должен выделять вызывающий. Размер out–параметров при вызове известен, в отличие от размера результата.

 

 

Я к тому, что LLVM не может передавать out и in/out параметры по

значению.

 

--

Maxim Reznik

On Sat, Mar 28, 2009 at 08:29:32AM +0100, Dmitry A. Kazakov wrote:

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

 

 

Если сравнить эту модель с моделью с одним стеком, какие есть +/- ? Минусы двустековой модели

* два стека не поддерживаются ЦПУ и ОС, прийдеться городить их на уровне библиотеки компилятора, следовательно

* это будет не так эффективно, как push, pop, ret. (Не помню, сменить указатель стека ЦПУ, это привелигированная операция?)

* контроль переполнения собственными средствами, а не механизмами ОС, (всякие guard page)

* стеки бесконечно растут вниз, расположить в памяти два "бесконечных" стека труднее чем один. Обычно в ОС стек растет вниз, куча растет вверх, можно запросить увеличения стека. А как реализовать два стека?

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

* как это сочитается с pragma Storage_Size (X), которая фактически задает размер стека задачи? Сделать два стека длинной Х/2?

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

зарезервировав Х байт а сьев Х/2+10 получит Storage_Error...

Минусы одностековой модели:

* промежуточные переменные вызываемой функции (вернувшей объект

переменного размера) не удаляются, пока вызвавшая функия не удалит объект-результат.

 

Давайте обсудим? Буду благодарен.

 

--

Maxim Reznik

On Mon, 30 Mar 2009 13:32:06 +0300, you wrote:

 

On Sat, Mar 28, 2009 at 08:29:32AM +0100, Dmitry A. Kazakov wrote:

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

 

Если сравнить эту модель с моделью с одним стеком, какие есть +/- ? Минусы двустековой модели

* два стека не поддерживаются ЦПУ и ОС, прийдеться городить их на уровне библиотеки компилятора, следовательно

* это будет не так эффективно, как push, pop, ret. (Не помню, сменить указатель стека ЦПУ, это привелигированная операция?)

* контроль переполнения собственными средствами, а не механизмами ОС, (всякие guard page)

* стеки бесконечно растут вниз, расположить в памяти два "бесконечных" стека труднее чем один. Обычно в ОС стек растет вниз, куча растет вверх, можно запросить увеличения стека. А как реализовать два

стека?

 

В случае с многими задачами - та же история.

 

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

 

* раздувание кеша: при одном стеке, переменные соседних вызовов

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

 

При наличии виртуальной памяти это - не проблемы. В принципе, RTL нужен и никто его не отменял.

 

* как это сочитается с pragma Storage_Size (X), которая фактически задает размер стека задачи? Сделать два стека длинной Х/2?

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

зарезервировав Х байт а сьев Х/2+10 получит Storage_Error...

 

Ну Storage_Size - дело темное. Никто не обязан его поддерживать на все 100.

Минусы одностековой модели:

* промежуточные переменные вызываемой функции (вернувшей объект

переменного размера) не удаляются, пока вызвавшая функия не удалит объект-результат.

 

Минусы

 

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

- делает сильно проблематичной отладку и трассировку

 

Плюсы

 

- проще inline и tail-recursion оптимизации

- проще часть стека положить в регистры (тоже оптимизация)

- меньше стеков, особенно, если еще и задачи учитывать

 

--

Regards,

Dmitry A. Kazakov

http://www.dmitry-kazakov.de

Maxim Reznik пишет:

On Sat, Mar 28, 2009 at 08:29:32AM +0100, Dmitry A. Kazakov wrote:

 

Не знаю как в GNAT и LLVM, но, да, техника такая, что используются два

стека (и, возможно, третий для точек возврата). На одном сидят локальные

переменные, туда же садится результат вызова. На другом находятся временные

объекты и собственный результат (ниже). Стеки обмениваются при каждом

вызове. Так что возвращать объекты переменного размера без копирования - не

проблема. И оператор возврата тоже не проблема.

 

 

Если сравнить эту модель с моделью с одним стеком, какие есть +/- ?

Минусы двустековой модели

* два стека не поддерживаются ЦПУ и ОС, прийдеться городить их на уровне

библиотеки компилятора, следовательно

* это будет не так эффективно, как push, pop, ret. (Не помню, сменить

указатель стека ЦПУ, это привелигированная операция?)

 

Эффективность не так критична. Второй стек используется только для результатов с неизвестным размером, а это относительно редко.

 

* контроль переполнения собственными средствами, а не механизмами ОС,

(всякие guard page)

 

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

 

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

стека труднее чем один. Обычно в ОС стек растет вниз, куча растет

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

стека?

 

Лучше посмотреть подробности в GNAT RTL. Насколько я понимаю, память на вторичный стек выделяется помаленьку, а в случае нехватки берётся блок из кучи.

 

 

Минусы одностековой модели:

* промежуточные переменные вызываемой функции (вернувшей объект

переменного размера) не удаляются, пока вызвавшая функция не удалит

объект-результат.

 

Как вариант, размещать результат сразу на куче, без оптимизации в виде стека.

 

-- If you want to get to the top, you have to start at the bottom

On Thu, Apr 02, 2009 at 11:00:47PM +0700, Иван Левашев wrote:

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

 

 

Как я понял объяснения Димы, оба стека одинаковы, т.к. они

меняются местами при каждом вызове.

 

Возможно вы имеете ввиду другой механизм. Тогда можно подробнее?

 

--

Maxim Reznik

On Mon, Mar 30, 2009 at 02:38:01PM +0200, Dmitry A. Kazakov wrote:

Минусы

 

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

 

Возможно я непонятно выразился, но тут тоже не нужно копирование. Смысл такой. Процедура А вызывает функцию B, которая возвращает

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

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

 

Стек будет вида:

* адрес возврата из А

* локальные переменные А

* мусор типа адреса возврата из Б и локальных переменных Б

* результат возвращенный Б

--> указатель стека

 

Процедура А может почистить результат Б и мусор Б сдвинув указатель стека назад, как только ей результат будет не нужен. А может

и не чистить вообще, все уйдет при выходе из А.

 

--

Maxim Reznik

On Fri, 3 Apr 2009 14:47:13 +0300, you wrote:

 

On Mon, Mar 30, 2009 at 02:38:01PM +0200, Dmitry A. Kazakov wrote:

Минусы

 

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

 

Возможно я непонятно выразился, но тут тоже не нужно копирование. Смысл такой. Процедура А вызывает функцию B, которая возвращает

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

 

Стек будет вида:

* адрес возврата из А

* локальные переменные А

* мусор типа адреса возврата из Б и локальных переменных Б

* результат возвращенный Б

--> указатель стека

 

Процедура А может почистить результат Б и мусор Б сдвинув указатель стека назад, как только ей результат будет не нужен. А может

и не чистить вообще, все уйдет при выходе из А.

 

Для этого А должен знать как устроен Б. Это очень плохо для раздельной компиляции. Кроме того, это не симметрично. Т.к. по-аналогии, А не должен сам себя чистить. Вместо этого, его должен чистить тот, кто его вызвал. И т.д. Либо момент чистки отодвигается в неизвестность, либо А должен чистить Б сразу после возврата, чего он не может сделать пока результат Б не использован.

 

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

 

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

 

--

Regards,

Dmitry A. Kazakov

http://www.dmitry-kazakov.de

Новое сообщение:
Страницы: 1

Чтобы оставить новое сообщение необходимо Зарегистрироваться и Войти