Comments 46
А поддержки llvm хоть в каком-то виде там нет?
«Очень хороший вопрос! Я ждал этого вопроса. Пожалуйста, следующий вопрос!» ©
Я не разбираюсь в теме, но мне кажется, что разработку / поддержку двух сильно разных компиляторов МЦСТ не потянет — хорошо бы хоть LCC пилить, не отставая от нововведений C++XX.
Я не разбираюсь в теме, но мне кажется, что разработку / поддержку двух сильно разных компиляторов МЦСТ не потянет — хорошо бы хоть LCC пилить, не отставая от нововведений C++XX.
Так реализация llvm как раз позволит не пилить свой компилятор, а воспользоваться clang, а может быть и gcc (не знаю, на сколько его llvm backend стабилен).
Да и другие языки можно было бы использовать, скажем, Rust.
Да и другие языки можно было бы использовать, скажем, Rust.
> для высокоуровневого разбора исходных кодов используется фронтэнд от Edison Design Group
Они не пилят.
Кроме того, видимо, биткод не очень хорош для влива, и используются другие структуры данных, более подходящие.
Они не пилят.
Кроме того, видимо, биткод не очень хорош для влива, и используются другие структуры данных, более подходящие.
Для VLIW нельзя не пилить свой компилятор, одна из главных черт этого класса архитектур — упрощение структуры за счет того, что значительная часть работы перекладывается на компилятор, который в принципе является неотъемлемой частью процессора.
Нужно поддерживать только backend. В представлении LLVM достаточно информации для оптимизации.
А что я то? Я точно не гуру. :) Ну а так, конечно LLVM можно хотя и очевидно, что сложнее чем не VLIW.
С другой стороны в LLVM уже есть несколько VLIW архитектур и в последнее время была проделана работа в этом направлении.
Ошибочное утверждение. Хотя конечно с LLVM не всё будет просто.
Меня бесит больше то, что они юзают GPL ный GCC, а исходники не открывают.
С другой стороны в LLVM уже есть несколько VLIW архитектур и в последнее время была проделана работа в этом направлении.
Для VLIW нельзя не пилить свой компилятор
Ошибочное утверждение. Хотя конечно с LLVM не всё будет просто.
Меня бесит больше то, что они юзают GPL ный GCC, а исходники не открывают.
Если я не ошибаюсь, то можно тот же rust скомпилить в C, с помощью соответствующего бэкенда к llvm, а получившийся код потом уже, например, в этот LCC засунуть.
Я слышал о том что некие коллективы делали что то на базе LLVM для МЦСТ. Вполне вероятно, что речь была о бакенде для Эльбруса.
P.S.: По поводу инструкции return в середине. Могу предположить, что здесь происходит что-то аналогичное delay slot из мира MIPS (обратите там внимание на примеры). Если инструкция упала в конвейер в общем кортеже, то она будет ждать выполнения хвоста. А потом управление будет передано в соответствии с return.
P.S.: По поводу инструкции return в середине. Могу предположить, что здесь происходит что-то аналогичное delay slot из мира MIPS (обратите там внимание на примеры). Если инструкция упала в конвейер в общем кортеже, то она будет ждать выполнения хвоста. А потом управление будет передано в соответствии с return.
nop 5 — скорее всего, действительно ожидание результата. Но вот return %ctp — это гораздо хуже — это подготовка перехода, который можно выполнить когда угодно. Если бы там была только игра с задержками, им бы пришлось ставить return за фиксированное число тактов конвейера до актуального момента перехода (решение более элегантное, хотя и менее гибкое).
Насчет операции return, — погуглите «split branch sh5», это нечто похожее. Общий смысл такой: процессоре есть три так называемых «станка подготовки перехода», для можно запустить подготовку перехода заранее, а затем инструкцией «ct» выполнить переход на один такт (если он будет подготовлен к этому моменту).
а если проще: он крут или нет?
Если исполнение команд процессора хоть чем-то похоже на процессоры TI C6xxxx (а на первый взгляд, сходства очень много), то разобраться в ассемблерных программах без очень подробного описания будет совершенно нереально. Одно только отложенное получение результата чего стоит (это когда в конвейере устройства находится сразу несколько команд на разных стадиях выполнения). И куча команд nop 5 указывает, что ждать результатов действительно приходится.
Надо книжку почитать.
Надо книжку почитать.
И куча команд nop 5 указывает, что ждать результатов действительно приходится.Это могут быть delay slot-ы, о которых я выше уже отписался.
Почитал… Ну они и наворотили. Команды для явного управления памятью L2… надо же такое придумать.
А почему команда return стоит в начале — это отложенная команда перехода. Она заносит в %ctpr3 (не знаю, что это такое) информацию о переходе и начинает загружать и обрабатывать команды, которые будут выполняться после перехода. И по команде ct %ctpr3 выполняется уже сам переход — конвейер уже полностью подготовлен.
А почему команда return стоит в начале — это отложенная команда перехода. Она заносит в %ctpr3 (не знаю, что это такое) информацию о переходе и начинает загружать и обрабатывать команды, которые будут выполняться после перехода. И по команде ct %ctpr3 выполняется уже сам переход — конвейер уже полностью подготовлен.
Даааа, вспоминаются лабораторные ВУЗовские по TMS, где чтение из памяти на 4 такта отложено, и эти 4 такта можно оперировать старым значением регистра.
Плюс 5 тактов на выполнение перехода. И при этом цикл из 8 команд при большой удаче можно упаковать в один такт.
Про переход за 5 тактов не помню, почему-то кажется, что в 1 делалось. По крайней мере, на том процессоре.
Да, упаковка хороша. Помню, была задача обработать массив с количеством тактов меньше кол-ва элементов в массиве. Ох, сидели мы, строили графики «лесенкой», паковали инструкции и в итоге-таки добились того, что с точки зрения программиста на x86 просто невозможно.
Да, упаковка хороша. Помню, была задача обработать массив с количеством тактов меньше кол-ва элементов в массиве. Ох, сидели мы, строили графики «лесенкой», паковали инструкции и в итоге-таки добились того, что с точки зрения программиста на x86 просто невозможно.
Выполняется переход, конечно, за 1 такт. Но заказывать его надо за 5 тактов до перехода. На процессорах TMS320C6xxx.
А обработать массив быстрее, чем за такт на элемент — тут сильно зависит от наличия команды загрузки двойного регистра (LDDW) — кажется, она появилась только в C64xx. Без неё удастся в лучшем случае посчитать сумму элементов.
А обработать массив быстрее, чем за такт на элемент — тут сильно зависит от наличия команды загрузки двойного регистра (LDDW) — кажется, она появилась только в C64xx. Без неё удастся в лучшем случае посчитать сумму элементов.
Не помню такого, вроде за 1 такт и непосредственно по «заказу». Но модель не помню, допускаю, что есть различия.
А там какой-то примитив навроде суммы и был. Все равно, по сравнению с х86 удивительно было.
А там какой-то примитив навроде суммы и был. Все равно, по сравнению с х86 удивительно было.
Давненько я не брал в руки шашек…
Можно даже сказать, никогда.
Можно даже сказать, никогда.
Тем не менее, код работает.
sum:
CMPGT .L2 B4,0,B0
|| ZERO .L1 A5
|| ZERO .S2 B5
|| ZERO .S1 A3
[!B0] B .S2 _END
NOP 5
MVC CSR,B6
AND B6,-2,B0
MVC B0,CSR
AND .L2 B4,1,B1
|| ADD .S2X 4,A4,B2
|| SHR .S1X B4,1,A2
[B1] LDW .D1 *A4++[2],A3
|| [!A2] B .S2 _END
|| [A2] SUB .L1 A2,1,A2
LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
_LOOP:
ADD .L1 A5,A0,A5
|| ADD .L2 B5,B0,B5
|| LDW .D1 *A4++[2],A0
|| LDW .D2 *B2++[2],B0
|| [A2] SUB .S1 A2,1,A2
|| [A2] B .S2 _LOOP
_END:
B .S2 B3
MVC B6,CSR
ADD .L1 A5,A3,A4
ADD .L1X A4,B5,A4
NOP 2
Что-то типа такого. Кстати, судя по всему вы правы — переход через 5 тактов:
Не уверен, что это хороший код, но рабочий. У меня лежит примерно 30 вариантов, в каждом по несколько циклов ужимается, а вот какой финальный — не помню. Интересный был предмет.
.ref _c_int00 ;точка входа
_c_int00:
;///////////////////////////////////////
.data ;секция данных
array1: .ushort 1,2,3,1,2,6,7,8,1,1,1,1,1,2,1,2,3,4,5,6,7,8,1,2,6 ;создаем массив 32 разрядных чисел
size .set 23 ;размер массива(>1)(препроцессорная константа)
;///////////////////////////////////////
.text ;секция кода
;Инициализация:
MVKL .S1 array1,A7 ;загружаем адрес массива1 в A3
MVKH .S1 array1,A7
; ADD .S1 A3, 2, A7
MVK .S2 size,B2 ;загружаем колво элементов массива в A2
; SUB .L2 B2,1,B2
MVK .S1 1,A5 ;ПРОИЗВЕДЕНИЕ ТЕКУЩИХ элементов массива
MVK .S1 2,A6 ;сумма произведений
MVKL .S1 0X20001, A12
MVKH .S1 0X20001, A12
MVK .S1 0,A1
MVK .S1 0,A4 ;GGGG
MVK .S1 0,A8
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
NOP 4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
NOP 4
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
NOP 4
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
NOP 4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||ADD .S1 A7,2,A7
LOOP:
[B2] B .S2 LOOP
||SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
SUB .D2 B2,1,B2
||LDW .D1 *A7, A0
||CMPEQ .L1 A0,A12,A2
||ADD .S1 A7,2,A7
||[A2] ADD .L2 B4,1,B4
THEEND:
NOP
Не уверен, что это хороший код, но рабочий. У меня лежит примерно 30 вариантов, в каждом по несколько циклов ужимается, а вот какой финальный — не помню. Интересный был предмет.
Непонятно, как он может быть рабочим. Ведь в этих процессорах нельзя читать память по невыровненному адресу (точнее, можно — но только начиная с C64, и используя команду невыровненного чтения — LDNW, а не LDW).
Серьёзная задача. Похоже, что можно упаковать обработку 8 элементов в 6 тактов. Но это будет очень непросто.
Примерно так.
.global find12
find12:
AND .L2 B4,3,B2
|| SHR .S1X B4,2,A2
|| ADD .D2XA4,4,B4
|| ZERO .L1 A0
|| ZERO .S2 B0
|| MV .D1 A6,A8
ZERO .L2 B2
|| SUB .L1 A2,2,A2
|| MV .D2XA6,B8
|| MVC CSR,B9
AND B9,-2,B1
MVC B1,CSR
|| LDW .D1 *A4++[2],A5 ;; [0@0]
|| LDW .D2 *B4++[2],B5 ;; [0@0]
NOP 2
LDW .D1 *A4++[2],A5 ;; [0@3]
|| LDW .D2 *B4++[2],B5 ;; [0@3]
NOP
CMPEQ .L1 A5,A8,A1 ;; [5@0]
|| SHR .S1 A5,16,A7 ;; [5@0]
LDW .D1 *A4++[2],A5 ;; [0@6]
|| LDW .D2 *B4++[2],B5 ;; [0@6]
|| ADD .S1 A0,A1,A0 ;; [6@0]
|| B .S2 _F12LOOP
SUB .D2 B5,B8,B1 ;; [7@0]
|| SHL .S1X B5,16,A6 ;; [7@0]
|| SHR .S2 B5,16,B7 ;; [7@0]
|| ZERO .L2 B6 ;; [7@0]
|| [A2] SUB .D1 A2,1,A2 ;; [7@0]
CMPEQ .L1 A5,A8,A1 ;; [5]
|| SHL .S2X A5,16,B6 ;; [5]
|| SHR .S1 A5,16,A7 ;; [5]
|| [!B1] ADD .L2 B0,1,B0 ;; [8]
|| ADD .D1 A6,A7,A6 ;; [8]
|| ADD .D2 B2,B6,B2 ;; [8]
_F12LOOP:
LDW .D1 *A4++[2],A5 ;; [0]
|| LDW .D2 *B4++[2],B5 ;; [0]
|| ADD .S1 A0,A1,A0 ;; [6]
|| ADD .L2 B6,B7,B6 ;; [6]
|| CMPEQ .L1 A6,A8,A1 ;; [9]
|| [A2] B .S2 _F12LOOP ;; [9]
SUB .D2 B5,B8,B1 ;; [7]
|| SHL .S1X B5,16,A6 ;; [7]
|| SHR .S2 B5,16,B7 ;; [7]
|| CMPEQ .L2 B6,B8,B6 ;; [7]
|| [A2] SUB .D1 A2,1,A2 ;; [7]
|| ADD .L1 A0,A1,A0 ;; [10]
CMPEQ .L1 A5,A8,A1 ;; [5]
|| SHL .S2X A5,16,B6 ;; [5]
|| SHR .S1 A5,16,A7 ;; [5]
|| [!B1] ADD .L2 B0,1,B0 ;; [8]
|| ADD .D1 A6,A7,A6 ;; [8]
|| ADD .D2 B2,B6,B2 ;; [8]
;; end loop
B .S2 B3
CMPEQ .L1 A6,A8,A1 ;; [9]
ADD .L2 B0,B2,B0
ADD .L1 A0,A1,A0 ;; [10]
ADD .L1X A0,B0,A4
MVC .S2 B9,CSR
3 такта на 4 полуслова.
Правда, этот код работает только для массивов длиной, кратной 4 и не меньше 12, но добавить дополнительную обработку для коротких массивов — дело техники.
А уж как в этих процах (TI) тормозит кастрированный C++ код… Интересно как тут оптмизатор с плюсами справляется.
А как с ними вообще можно справиться? Для вызова виртуальной функции нужно, как минимум, двойное обращение к памяти (плюс какой-нибудь анализ на случай множественного наследования), а потом — сам вызов. Тоже весьма долгий, так как адрес заранее неизвестен. Хорошо, если параллельно с подготовкой вызова можно ещё что-нибудь поделать. А если нет — остаётся только ждать.
Очень круто вы все описываете — тема сложная, но изложено все грамотно и системно, в отличие от многих подобных статей на хабре. Спасибо!
Сколько же этот Эльбрус был у вас «проездом», что так детально удалось покопаться? :)
Сколько же этот Эльбрус был у вас «проездом», что так детально удалось покопаться? :)
(книгу не смотрел)
Вангую, что расположение инструкции return явно учитывает наличие конвейера. return начинает загружать инструкции из точки возврата в конвейер, а пока до них дойдёт управление, выполняются идущие перед ними инструкции текущей функции.
Вангую, что расположение инструкции return явно учитывает наличие конвейера. return начинает загружать инструкции из точки возврата в конвейер, а пока до них дойдёт управление, выполняются идущие перед ними инструкции текущей функции.
Есть ли возможность скачать где-то LCC от МЦСТ, в виде кросс компилятора с x86/amd64 или в виде исходников?
А в чём выражется совместимость с GCC? Можно ли Аду открутить от GCC и поставить на LCC?
Имеется в виду способность выглядеть как GCC, плавать как GCC (но не крякать как GCC, правда) — с точки зрения языков C, C++, Fortran. То есть поддерживаются все те же ключи запуска и дополнения к языковым стандартам. Компилируемая программа думает, что ею занимается GCC; хотя, если она в курсе про существование LCС, то, конечно, может детектировать его по соответствующим define'ам.
Мда. Очень печально, что адскими проверками нельзя насладиться на архитектуре, которая реализует их аппаратно.
Интересно было бы сравнить Эльбрус+LCC и CHERI+BERI. CHERI поизвестнее LCC будет, однако внезапно Эльбрус у нас оказался более массовым, а я думаю, Эльбрусов выпущено гораздо больше, чем BERI.
Интересно было бы сравнить Эльбрус+LCC и CHERI+BERI. CHERI поизвестнее LCC будет, однако внезапно Эльбрус у нас оказался более массовым, а я думаю, Эльбрусов выпущено гораздо больше, чем BERI.
Sign up to leave a comment.
Обзор и сравнительное тестирование ПЭВМ «Эльбрус 401‑PC». Часть третья — средства разработки