Просмотр полной версии : LLVM Backend для Z80
Сейчас работаю над сабжем. Если кто-то не в курсе, то вот пара ссылок:
http://ru.wikipedia.org/wiki/LLVM
http://habrahabr.ru/post/47878/
http://llvm.org/
На данном этапе возник вопрос как эффективнее выделять память на стеке, т.е. интерпретировать команду alloca (http://llvm.org/docs/LangRef.html#i_alloca):
<result> = alloca <type>[, <ty> <NumElements>][, align <alignment>]
"Честный" способ выделения памяти выглядит примерно так:
push hl
ld hl,-size
add hl,sp
ld sp,hl
...
ld hl,size
add hl,sp
ld sp,hl
pop hl
ret
В данном случае существует минус в том, что обращаться к переменным выделенным на стеке придется следующим образом:
ld hl,offset
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
Получается 7 байт на то, чтобы считать слово на стеке. Как-то не очень хорошо))
С другой стороны можно вместо HL использовать IX, тогда обращение будет выглядеть более приятно:
ld e,(ix+offset)
ld d,(ix+offset+1)
6 байт и всего две команды, но тут у нас выделение и освобождение памяти на стеке будет чуть больше размером, т.к. работать будем с IX.
Если предположить, что наша программа будет находится в озу, то можно придти к такому варианту выделения памяти:
ld (.stack),sp
ld hl,-size
add hl,sp
ld sp,hl
...
ld sp,0
.stack $-2
ret
Дополнительно к этому можно добавить опцию, указывающую будет ли программа работать в озу или пзу, чтобы генерировать нужный код.
Собственно вопросов всего три:
1) Как лучше выделять память на стеке? Какой регистр использовать - HL или IX?
2) Как обращаться к переменным на стеке
3) Использовать лучше "честный" метод или сделать возможность включения более быстрого?
Если еще есть какие-то замечения и/или пожелания по данному вопросу, буду рад выслушать.
Интересная тема.
А на какой стадии находитесь ? Что-то уже работает или только в начале ?
По теме:
Если сможете адресовать по HL, то будет хорошо.
По IX тоже можно (чуть медленнее код будет).
Думаю главное стабильность.
P.S. Когда-то пробовал
LLVM C++ ==> C backend ==> sdcc
(хотелось на плюсах программить) но там свои проблемы были
А что мешает заимплементить оба варианта и переключаться между ними с помощью опций?
Интересная тема.
А на какой стадии находитесь ? Что-то уже работает или только в начале ?
Сейчас реализовано немного.. Хм.. Возможность возвращения типов i8, i16. Обработка формальных переменных, в т.ч. есть возможность передавать некоторые параметры в регистрах, остальные на стеке. По сути надо доделать выделение памяти на стеке и дальше уже просто добавлять новые и новые команды.
А что мешает заимплементить оба варианта и переключаться между ними с помощью опций?
Я думаю это будет лучшим вариантом, но по началу не хочу на этом зацикливаться и просто добавить какой-то один способ выделения.
PS. Все наработки сейчас лежат вот здесь http://sourceforge.net/projects/llvmz80/
goblinish
28.09.2012, 22:05
А что мешает заимплементить оба варианта и переключаться между ними с помощью опций?
Хабр головного мозга, очевидно же..
NovaStorm
28.09.2012, 22:26
1) Как лучше выделять память на стеке?
Сначала вариант №1. Потом опцией самомодифицирующийся код, но это под вопросом.
Какой регистр использовать - HL или IX?
2) Как обращаться к переменным на стеке
Сам вчера-сегодня интересовался подобным вопросом, IX удобнее вышел.
но тут у нас выделение и освобождение памяти на стеке будет чуть больше размером, т.к. работать будем с IX.
Выделяй через IX, освобождай через HL.
ИМХО, сделай, чтобы всё работало, а потом уж будешь оптимизировать.
или остановишься на какомто варианте, или опцией разрулишь.
NovaStorm, спасибо. Примерно так наверно и сделаю для начала.
ИМХО, сделай, чтобы всё работало, а потом уж будешь оптимизировать.
или остановишься на какомто варианте, или опцией разрулишь.
Да, оптимизацию буду делать уже на рабочем варианте)
PS Если есть кто-то, кто знаком с LLVM или просто желает помочь в разработке бэкенда для Z80, то пишите в эту тему или на почту earl1k[собака]mail.ru. Буду рад любой помощи. Одна голова хорошо, а много - лучше.:smile:
NovaStorm, спасибо. Примерно так наверно и сделаю для начала.
Да, оптимизацию буду делать уже на рабочем варианте)
PS Если есть кто-то, кто знаком с LLVM или просто желает помочь в разработке бэкенда для Z80, то пишите в эту тему или на почту earl1k[собака]mail.ru. Буду рад любой помощи. Одна голова хорошо, а много - лучше.:smile:
Да за gсс для z80 тебе пива не только я поставить готов)
Да за gсс для z80 тебе пива не только я поставить готов)
Ну это не gcc будет конечно, но clang тоже неплох, но сперва нужен рабочий LLVM backend
Ну это не gcc будет конечно, но clang тоже неплох, но сперва нужен рабочий LLVM backend
а почему не gcc? llvmу ведь поффиг какой фронт-енд использовать?)
хотя, я думаю, что c-lang непринципиально отличается в плане применения на ZX
NovaStorm
01.10.2012, 21:30
Не хочу отвадить от работы, тьфу-тьфу... =)
Но! llvm есть только для 32+ битных процов, на них он и ориентирован, с 8ми битным могут быть проблемы.
Для gcc есть порт на 6809, до z80 его допилить может быть легче.
Желательно ознакомиться с sdcc и его abi, чтобы не бегать по тем же граблям. Там же можно подглядеть и peephole оптимизации.
mastermind
01.10.2012, 22:02
Буду рад любой помощи.
Вот потенциальный соучастнег: http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-April/049228.html
(исходник там в аттаче)
а почему не gcc? llvmу ведь поффиг какой фронт-енд использовать?)
Точно не скажу, т.к. не пробовал прикручивать gcc к llvm, но вроде сами разработчики решили отказаться от поддержки gcc и направить все силы на поддержку clang. Хотя возможность прикрутить и использовать gcc все же осталась как я понял))
llvm есть только для 32+ битных процов, на них он и ориентирован, с 8ми битным могут быть проблемы.
Ну точно можно сказать что поддержка 16 битных процов уже реализовали в llvm. Взять тот же MSP430 backend. Да и так я не нашел никаких проблем прикрутить кодогенерацию для любого процессора, т.к. например сейчас у меня код llvm ассемблера преобразуется в ассемблерный файл для z80.
Для gcc есть порт на 6809, до z80 его допилить может быть легче.
Желательно ознакомиться с sdcc и его abi, чтобы не бегать по тем же граблям. Там же можно подглядеть и peephole оптимизации.
Хм.. не слышал про данный порт для gcc. Надо будет поискать. А вот на счет ABI уже задумывался. Я так понимаю ABI как такового нет и все компиляторы сейчас делают каждый по своему. Стоит ли делать совместимость ABI с другими компиляторами или нет вопрос спорный. Например в llvm можно легко реализовать передачу части параметров функции в регистрах очень легко, а не тупо все пихать в стек. Данной возможности пока не видел ни в каком компиляторе. Думаю это может дать не малый прирост к скорости.
Но совместимость ABI можно также сделать позже.
---------- Post added at 22:15 ---------- Previous post was at 22:10 ----------
Вот потенциальный соучастнег: http://lists.cs.uiuc.edu/pipermail/l...il/049228.html
(исходник там в аттаче)
Хм.. Как-то беглым взглядом смутили строки вида:
ld (SP+6), BC
Но всё равно спасибо. Посмотрю его исходники и попробую связаться.
mastermind
01.10.2012, 22:20
llvm есть только для 32+ битных процов, на них он и ориентирован, с 8ми битным могут быть проблемы.
Глупости, бэкенд может вообще не иметь ничего общего с существующими процессорами (подробнее см. http://llvm.org/docs/CodeGenerator.html#required-components-in-the-code-generator ).
В старых версиях кстати был бэкенд для PIC16 (8 бит), но позже был выкинут, если не ошибаюсь из за того что автор его забросил и перестал апдейтить для новых версий llvm. (см. http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/PIC16/?pathrev=116189 )
Не говоря уже о том что есть бэкенды для генерации C-кода (кажется тоже заброшенный), и Javascript-кода (очень даже работающий и поддерживаемый: https://github.com/kripken/emscripten/wiki - демки, кстати, там советую посмотреть)
NovaStorm
02.10.2012, 08:16
Стоит ли делать совместимость ABI с другими компиляторами или нет вопрос спорный.
Я не про совместимость, на которую сейчас плевать, а про посмотреть, как работа с тем же стеком сделана.
Я не про совместимость, на которую сейчас плевать, а про посмотреть, как работа с тем же стеком сделана.
По-моему неважно как она сейчас сделана. Надо сначала сделать полноценный кодогенератор, а потом уж заниматься оптимизациями. Иначе легко погрязнуть в мелочах, не дойдя до конца.
Когда уже есть что-то работающее - тогда видны его недостатки и их можно исправлять. А теоретезировать на тему - "давайте так", "давайте этак" - можно месяцами. Концептологи этим и занимаются. Только выхлоп у них близкий к нулю:)
Аффтор! Не бросай lllvm. Дай нам кафйфный C для zx) Кстати, как появится что-то рабочее, если не жалко - делай проект с исходниками в открытом доступе. Поругают, конечно, знатно, зато и больше советов дадут дельных по нутру)
Аффтор! Не бросай lllvm. Дай нам кафйфный C для zx) Кстати, как появится что-то рабочее, если не жалко - делай проект с исходниками в открытом доступе. Поругают, конечно, знатно, зато и больше советов дадут дельных по нутру)
Да исходники и сейчас уже доступны. Ссылку вышел давал, вот еще раз если интересно - http://sourceforge.net/projects/llvmz80/?source=directory
На данный момент там правда только добавляются новые команды и что-то рабочее получить врят ли получится
Да исходники и сейчас уже доступны. Ссылку вышел давал, вот еще раз если интересно - http://sourceforge.net/projects/llvmz80/?source=directory
На данный момент там правда только добавляются новые команды и что-то рабочее получить врят ли получится
Скачал) буду посмотреть. надо llvm изучить немного.
как собрать? какие ключи в ./configure ?
./configure --enable-targets=z80
Только лучше собирать в отдельной директории. Если при сборке будут ошибки, то пишите сюда, исправлю.
Сам я использую CMake для сборки, поэтому в configure скриптах не всегда успеваю все править во время.
Автор, не теряйся! Как успехи?
Работаю потихоньку. Писал письмо Peter'у Hanzel'у, который также занимался разработкой backend'а для Z80, но он мне так и не ответил.
Также понял, что немного придется подправить clang для хорошей генерации llvm кода. Если точнее, то нужно добавить CodeGenTargetInfo ABIInfo и еще пару классов. В целом не очень сложно. Хорошо бы конечно пообщаться с кем-нибудь кто тоже разбирался с работой llvm, т.к. некоторые вопросы все же имеются и самому доходить до всего получается дольше. Но проект не забрасываю. Как будут первые более менее рабочие результаты пригодные для использования, то обязательно поделюсь
mastermind
17.10.2012, 20:15
Хорошо бы конечно пообщаться с кем-нибудь кто тоже разбирался с работой llvm
Дык есть же мейлинг-лист разработчиков, можно общаться со всеми сразу :) http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
mastermind, я в курсе про мейлинг лист. Что ж, попробую туда написать по интересующим вопросам.
Есть вопрос по поводу преобразования типов в вычислениях в языке Си. Нашел список правил, по которому осуществляется преобразование:
1. Операнды типа float преобразуются к типу double.
2. Если один операнд long double, то второй преобразуется к этому же типу.
3. Если один операнд double, то второй также преобразуется к типу double.
4. Любые операнды типа char и short преобразуются к типу int.
5. Любые операнды unsigned char или unsigned short преобразуются к типу unsigned int.
6. Если один операнд типа unsigned long, то второй преобразуется к типу unsigned long.
7. Если один операнд типа long, то второй преобразуется к типу long.
8. Если один операнд типа unsigned int, то второй операнд преобразуется к этому же типу.
Таким образом если у нас есть программа:
char add(char a, char b)
{
return a+b;
}
Операнды бинарной операции "сложения" должны будут преобразованы к типу int (по правилу 4), затем выполняется сама операция и результат преобразуется к типу char.
Компилятор clang именно так и делает, но при этом генерирует цепочку команд примерно такого вида:
define i8 test(i8 %a, i8 b)
{
%ext_a = sext i8 %a to i16 ; знаковое расширение типа
%ext_b = sext i8 %b to i16 ; знаковое расширение типа
%add = add i16 a, b
%trunc = trunc i16 %add to i8 ; усечение типа
ret i8 %trunc
}
В результате llvm кодогенератор начинает все эти действия честно переводить в код z80, т.е. сложение получается медленное и мучительное - с расширением в 16 битный регистр и сложением через ADD HL,RR))
Конечно это лишь пример и данный код оптимизатор сворачивают в одну 8-битную команду сложения, но есть примеры которые можно в ручную свести к 8-битной арифметике, но оптимизатор этого не делает. Пока встречал это в конструкциях с условиями.
Интересно следуют ли имеющиеся компиляторы Си всем вышеперечисленным правилам преобразования типов.
Также интересно вот что:
Размер переменной типа int в стандарте языка Си не определен. В большинстве систем программирования размер переменной типа int соответствует размеру целого машинного слова. Например, в компиляторах для 16-разрядных процессоров переменная типа int имеет размер 2 байта. В этом случае знаковые значения этой переменной могут лежать в диапазоне от -32768 до 32767.
Т.к. Z80 8-битный процессор, то int должен иметь размер 1 байт или я не прав? Или быть может Z80 какой-то промежуточный вариант, т.к. в наборе команд есть операции и для работы с 16 битными данными (напр. ADD HL,HL)?
mastermind
19.10.2012, 20:37
Т.к. Z80 8-битный процессор, то int должен иметь размер 1 байт или я не прав? Или быть может Z80 какой-то промежуточный вариант, т.к. в наборе команд есть операции и для работы с 16 битными данными (напр. ADD HL,HL)?
По крайней мере в современном стандарте, насколько я понимаю, есть требования к минимальным диапазонам значений целочисленных типов:
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
стр. 21, 22:
5.2.4.2.1 Sizes of integer types <limits.h>
The values given below shall be replaced by constant expressions suitable for use in #if
preprocessing directives. Moreover, except for CHAR_BIT and MB_LEN_MAX, the
following shall be replaced by expressions that have the same type as would an
expression that is an object of the corresponding type converted according to the integer
promotions. Their implementation-defined values shall be equal or greater in magnitude
(absolute value) to those shown, with the same sign.
...
— minimum value for an object of type short int
SHRT_MIN -32767 // −(2^15 − 1)
— maximum value for an object of type short int
SHRT_MAX +32767 // 2^15 − 1
— maximum value for an object of type unsigned short int
USHRT_MAX 65535 // 2^16 − 1
— minimum value for an object of type int
INT_MIN -32767 // −(2^15 − 1)
— maximum value for an object of type int
INT_MAX +32767 // 2^15 − 1
— maximum value for an object of type unsigned int
UINT_MAX 65535 // 2^16 − 1
Т.е. значения этих лимитов (абсолютные) могут быть либо равны указанным либо м.б. больше (но не меньше). Иначе говоря, int, unsigned int, а также short-типы д.б. не менее 16 бит.
(про остальные типы см. там же)
---------- Post added at 19:37 ---------- Previous post was at 19:28 ----------
но есть примеры которые можно в ручную свести к 8-битной арифметике, но оптимизатор этого не делает.
Путем допиливания оптимизатора (в будущем) эту проблему может можно будет решить? (путем реализации какого-нить кастомного pass-а, llvm в этом плане очень гибок, насколько я понимаю)
пускай будет медленно и мучительно, это проблемы компилятора, а не llvm.
Согласен. Например можно написать код на llvm с использованием 8-битной арифметики и код для z80 будет соответствующим.
mastermind, спасибо за ссылку) Как-то интуитивно тоже полагал что int не может быть меньше 16 бит. Значит оставлю пока все как есть.
Путем допиливания оптимизатора (в будущем) эту проблему может можно будет решить? (путем реализации какого-нить кастомного pass-а, llvm в этом плане очень гибок, насколько я понимаю)
Да. Либо pass к llvm, либо к clang. Пока точно не разобрался как правильнее и проще будет сделать. Но это реально)
PS. Если кому-то интересно то, что уже реализовано, что планируется и какие спорные вопросы сейчас решаются, то можете спрашивать в этой теме. Могу просто освещать новые реализованные возможностями с примерами для общего анализа и получения лучшего результата.:)
mastermind
19.10.2012, 21:12
Конечно, интересно все :) Тема очень интересная.
---------- Post added at 20:12 ---------- Previous post was at 19:59 ----------
Бегло просмотрел насчет преобразования типов операндов (в вышеупомянутом стандарте со стр. 42), вроде как совсем не обязательно char и т.п. преобразовывать "вверх" до int, сказано только о том что на месте int и unsigned int могут использоваться типы с меньшим "conversion rank", и в этих случаях они преобразовываются в int или unsigned int.
Т.е. если все операнды, скажем, восьмибитные и результат тоже, то по идее ничего никуда преобразоывать не нужно. (или я чего-то не дочитал) Так что возможно, вышеописанное преобразование всего до int не более чем особенность реализации clang, а не стандарт.
mastermind, судя по комментариям в исходниках clang преобразование к типу int сделано совместимо с gcc. Но сейчас это не так важно, т.к. сперва нужно допилить backend к llvm, а после уже можно будет разобраться с clang. :)
Это я просто как-то решил добавить в clang возможность генерировать llvm код для процессора Z80 и заметил такие "странности" в расширением/усечением типов. Сейчас вернулся к дальнейшей работе над backend'ом.
В llvm есть такие операции как усечение значений и расширение. Например усечением 16 битного слова будет являться 8 бит, т.е. 1 байт. Это легко реализуется, например, усеченным значением регистра HL будет регистр L.
Теперь о расширении значений. Всего имеется 3 вида расширений:
1. zext - расширение с обнулением старшей части. Например расширение регистра A в регистра HL будет выглядеть так:
LD L,A
LD H,#00
2. anyext - расширение с неопределенной старшей частью:
LD L,A
3. sext - знаковое расширение. Т.е. если 7 бит равен единице, то старшая часть заполняется единицами. Что-то вроде этого
LD L,A
LD H,#00
BIT 7,A
JR Z,NO_SIGN
LD H,#FF
NO_SIGN:
Пример написал для большего понимания. Посоветуйте как эффективнее и короче можно сделать знаковое расширение.
Может так?
; Изначально - в А число
LD L,A ; Младший байт
AND #80h ; Проверка старшего бита на знак
RLA ; CY = старший бит из А, А=0
SBC A,#00 ; A=A-CY (0, если +, FF, если -)
LD H,A ; Сташий байт HL= расширенное со знаком число
Может так?
; Изначально - в А число
LD L,A ; Младший байт
AND #80h ; Проверка старшего бита на знак
RLA ; CY = старший бит из А, А=0
SBC A,#00 ; A=A-CY (0, если +, FF, если -)
LD H,A ; Сташий байт HL= расширенное со знаком число
Да, я думаю этот вариант пока лучше подойдет, т.к. в нём нет никаких условных/безусловных переходов, т.е. все действия выполняются в пределах одного так называемого BasicBlock (http://ru.wikipedia.org/wiki/Basic_block). Если будут в дальнейшем варианты получше, то заменить всегда можно.:)
mastermind
21.10.2012, 15:46
Я когда-то тоже начинал делать backend. Выложил в dropbox: https://dl.dropbox.com/u/12936792/lib-Target-Z80.tar.gz
Предупреждение: там могут быть не то что ляпы, а тупейшие/грубейшие ошибки ;) Это не более чем заготовка, сделанная в процессе чтения документации (т.е. без всеобъемлющего понимания всех аспектов работы llvm) предназначенная для дальнейшего пиления.
Тем не менее надеюсь что там можно найти и позаимствовать какие-нить полезные идеи. :)
Alex Rider
21.10.2012, 16:00
Посоветуйте как эффективнее и короче можно сделать знаковое расширение.
ld l,a
rlca
sbc a,a
ld h,a
Я когда-то тоже начинал делать backend. Выложил в dropbox: https://dl.dropbox.com/u/12936792/lib-Target-Z80.tar.gz
Предупреждение: там могут быть не то что ляпы, а тупейшие/грубейшие ошибки ;) Это не более чем заготовка, сделанная в процессе чтения документации (т.е. без всеобъемлющего понимания всех аспектов работы llvm) предназначенная для дальнейшего пиления.
Тем не менее надеюсь что там можно найти и позаимствовать какие-нить полезные идеи. :)
Спасибо! Гляну исходники. Может быть что-то и правда пригодится)
ld l,a
rlca
sbc a,a
ld h,a
Вот это хороший вариант! Спасибо.
mastermind
21.10.2012, 16:06
Немного комментариев по поводу особенностей моей заготовки:
В документации где-то было сказано что "ultimate design goal" или вроде того - чтобы можно было модель cpu (backend) сделать полностью с помощью tablegen. (.td-файлы)
В русле стремления к этому например были приняты такие решения:
- группировать регистры (в классы) по наборам инструкций в которых они могут быть использованы (отсюда например такие классы как R16_BC_DE_HL, R16_BC_DE_IX_SP и т.д.) + соответствующая группировка инструкций,
- операции обмена между регистрами реализовать с помощью "виртуальных регистров", например (упрощенно):
def V_DEHL : RegisterWithSubRegs<"v_dehl", [DE,HL]>;
def V_HLDE : RegisterWithSubRegs<"v_hlde", [HL,DE]>;
let Defs = [DE,HL] in
def EX_DE_HL : I<0xEB, RawFrm, (outs), (ins), "ex\tde, hl", [(set V_DEHL, V_HLDE)]>;
- опкоды (точнее биты которые присутствуют (=1) во всех вариациях инструкций, иначе говоря этакие "базовые опкоды") также присутствуют в .td для дальнейшего использования в реализации MC и т.п.
Уважаемый аффтор!
Не забываем сюда писать, как дела:) Все ждут, затаив дыхание! :)
В последнее время занимался продумыванием и переписыванием .td файлов, в которых описываются все инструкции и регистры процессора Z80. Это позволяет многие упрощения (lowering) реализовать автоматически, т.е. преобразовывать команды llvm в команды процессора Z80 лишь путем описания в .td файлах, что очень удобно и упрощает разработку. Однако не для всех инструкций Z80 есть аналоги в llvm и наоборот не все llvm команды могут быть нативно выполнены на Z80. Для вторых создаются функции, которые заменяют llvm инструкцию на цепочку команд Z80. С первыми бывает немного сложнее, особенно если хочется их использовать для генерации более компактного кода. Поэтому оптимизацией никакой на данном этапе я не занимаюсь. Сейчас просто покажу пару примеров того, что уже может быть скомпилировано.
Пример №1:
unsigned char test(bool useFirst, unsigned char first, unsigned char second)
{
return (useFirst) ? first : second;
}
После генерации llvm кода clang'ом:
; ModuleID = 'test.cpp'
target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8-n8:16"
target triple = "z80"
define i8 @_Z4testbhh(i1 %useFirst, i8 %first, i8 %second) nounwind readnone {
entry:
%cond = select i1 %useFirst, i8 %first, i8 %second
ret i8 %cond
}
После генерации кода Z80 утилитой llc (аргументы передаются в регистрах A, B, C по порядку):
.file "test.ll"
.text
.globl _Z4testbhh
.type _Z4testbhh,@function
_Z4testbhh: ; @_Z4testbhh
; BB#0: ; %entry
and 1
cp 0
jp nz, .BB0_2
; BB#1: ; %entry
ld b, c
.BB0_2: ; %entry
ld a, b
ret
.tmp0:
.size _Z4testbhh, .tmp0-_Z4testbhh
Пример №2:
unsigned char test(unsigned char a, unsigned char b)
{
return (b^0xFF) + (a&0x0F);
}
После генерации llvm кода clang'ом:
; ModuleID = 'test.cpp'
target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8-n8:16"
target triple = "z80"
define i8 @_Z4testhh(i8 %a, i8 %b) nounwind readnone {
entry:
%xor = xor i8 %b, -1
%and = and i8 %a, 15
%add = add i8 %and, %xor
ret i8 %add
}
После генерации кода Z80 утилитой llc (аргументы передаются в регистрах A, B по порядку):
.file "test.ll"
.text
.globl _Z4testhh
.type _Z4testhh,@function
_Z4testhh: ; @_Z4testhh
; BB#0: ; %entry
and 15
ld c, a
ld a, b
cpl
ld b, a
ld a, c
add a, b
ret
.tmp0:
.size _Z4testhh, .tmp0-_Z4testhh
В данный момент осталось дописать команды условных и прямых переходов. Замечу, что на данный момент везде используются переходы JP, что также связано отказом от оптимизации на данный момент. В дальнейшем добавлю проход для генерации JR переходов.
Ну и самый пока неоднозначный для меня момент это работа с памятью через регистры. Очень не хочется все упрощения делать вручную, да и не обязательно это, но как это лучше реализовать в .td файлах пока думаю.
На этом пока все. Если у кого-то есть какие-то вопросы или пожелания, говорите.
mastermind
31.10.2012, 23:56
Однако не для всех инструкций Z80 есть аналоги в llvm и наоборот не все llvm команды могут быть нативно выполнены на Z80. Для вторых создаются функции, которые заменяют llvm инструкцию на цепочку команд Z80
Про rtlib в курсе, кстати? http://llvm.org/docs/doxygen/html/namespacellvm_1_1RTLIB.html
mastermind, нет, не в курсе. Если разбирался с этим, то думаю стоить обменяться контактами. Поможешь понять что это и с чем едят :)
mastermind
01.11.2012, 00:13
Всерьез не разбирался, но знаю что это либа (часть llvm) для эмуляции неподдерживаемых напрямую (на каком-либо процессоре) операций. Насколько я понимаю, она просто генерит ir-код используя доступные на процессоре операции. (т.е. полезно для быстрой реализации всяких умножений/делений, операций с разрядностью больше 8 и т.д.).
Чтоб найти примеры использования просто поищи RTLIB в коде существующих таргетов.
(ну контакты тоже сейчас скину в личку на всякий случай, хотя думаю что лучше на форуме все обсуждать, может это будет способствовать всеобщему brainstorming-у ;) )
Имеется код сравнения двух чисел:
LD A,arg1
CP arg2
JP (CC),label
CC - флаг условия.
arg1, arg2 - числа для сравнения вида arg1 [условие] arg2, где условие может быть одним из всевозможных вариантов (>, <, ==, !=, >=, <=).
Также сравниваемые значения могут быть двух типов: знаковые и беззнаковые.
Для обоих типов:
== - Z
!= - NZ
Для беззнаковых:
< - C
>= - NC
Для знаковых:
< - M (для знаковых)
> - P (для знаковых)
Требуется помощь в составлении полной таблицы условия - флаги.:)
Знаю, что для всех условий в Z80 не найдутся флаги, поэтому найти нужно хотя бы половину, а остальные можно будет инвертировать.
LD A,arg1
CP arg2
JP (CC),cont
JP label
cont:
http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html
Error404
02.11.2012, 14:04
Насколько я понимаю, она просто генерит ir-код используя доступные на процессоре операции. (т.е. полезно для быстрой реализации всяких умножений/делений, операций с разрядностью больше 8 и т.д.).
Вот это было бы очень здорово. Компилер нужен именно с поддержкой 32-битных типов (хотя бы целочисленных), ибо 8/16 битных С-компилеров для Z80/8080 и так пруд пруди.
Andrew771
02.11.2012, 15:36
Компилер нужен именно с поддержкой 32-битных типов (хотя бы целочисленных), ибо 8/16 битных С-компилеров для Z80/8080 и так пруд пруди.
А для чего нужны 32-битные типы на Спектруме? Что за программы будут писаться?
NovaStorm
02.11.2012, 16:41
Не обязательно на спеке. Например для ОС и ФС.
Насколько я понимаю, она просто генерит ir-код используя доступные на процессоре операции. (т.е. полезно для быстрой реализации всяких умножений/делений, операций с разрядностью больше 8 и т.д.).
Вот это было бы очень здорово. Компилер нужен именно с поддержкой 32-битных типов (хотя бы целочисленных), ибо 8/16 битных С-компилеров для Z80/8080 и так пруд пруди.
Разобрался я с этим RTLIB.:) Вообщем эта штука немного другое делает. Если в двух словах, то она для неподдерживаемых архитектурой команд вставляет вызовы функций с заданными именами. Т.е. можно будет написать библиотеку где будут эти функции и просто линковать скомпилированную программу с этой библиотекой. Например Z80 никак не поддерживает команду умножения и деления, а в llvm есть такие команды.:)
define i8 @test(i8 %a, i8 %b)
{
%res = mul i8 %a, %b
ret i8 %res
}
После компиляции получается вот что:
call _mathLib_MULi8
ret
Аргументы передаются в регистрах A, B. Функция возвращает результат в регистре A.
Однако llvm все же радует кое чем. Например если архитектура имеет только 16 битные регистры, то llvm сам умеет создавать цепочку команд для вычисления 32 битных (и более) чисел с помощью имеющихся регистров.
Error404
02.11.2012, 18:29
Однако llvm все же радует кое чем. Например если архитектура имеет только 16 битные регистры, то llvm сам умеет создавать цепочку команд для вычисления 32 битных (и более) чисел с помощью имеющихся регистров.
Это действительно радует.
---------- Post added at 18:29 ---------- Previous post was at 18:27 ----------
А для чего нужны 32-битные типы на Спектруме? Что за программы будут писаться?
Портирование системного ПО с других платформ зачастую упирается в отсутствие поддержки компилятором long (32 бита). Простейший пример - библиотеки ФАТ32. Именно портирование, а не писание с нуля - реальный шанс поиметь на Спеке (или Орионе или еще чем-то) какое-то новое ПО. Просто в силу того, что уже никто не выдаст нужное количество человекочасов, сколько нужно для написания чего-то серьезного с нуля - уже не тот запал.
Вот это было бы очень здорово. Компилер нужен именно с поддержкой 32-битных типов (хотя бы целочисленных), ибо 8/16 битных С-компилеров для Z80/8080 и так пруд пруди.
Ну уж нет! стандартный float тоже надо. а libm - портируется...
Ну уж нет! стандартный float тоже надо. а libm - портируется...
Стандартный float сделать можно, но для этих целей нужно будет делать библиотеку уже. Честно говоря я не силен в работе с типом float и хороших быстрых функций наваять сам не смогу, поэтому если у кого-то есть готовые библиотеки для работы с float, то буду очень этому рад.:) Хм.. что за libm?
Стандартный float сделать можно, но для этих целей нужно будет делать библиотеку уже. Честно говоря я не силен в работе с типом float и хороших быстрых функций наваять сам не смогу, поэтому если у кого-то есть готовые библиотеки для работы с float, то буду очень этому рад.:) Хм.. что за libm?
Сам C поддерживает только + - * / для флоата. Остальное - внешнаяя библиотека libm.
Сделаешь эти 4 функции (пусть не оптимально пока что) - и хватит. Главное, чтобы флоат - был Сшним флоатом по стандарту, а не каким-то извратом.
Та сейчас на оптимизации не морочься - главное функционал. Пока функционала нет - и оптимизировать нечего.
SfS, я думаю все операции с float все равно лучше делать в виде отдельной библиотеки. А на счет формата float, то в любом случае это будет IEEE 754, т.е. как раз стандартный, а не то что используется в бейсике.
SfS, я думаю все операции с float все равно лучше делать в виде отдельной библиотеки. А на счет формата float, то в любом случае это будет IEEE 754, т.е. как раз стандартный, а не то что используется в бейсике.
ну как оно внутри будет - неважно) а вот IEEE 754 - это руль)
На данный момент уже могут компилироваться многие программы работающие с 8 битной арифметикой. Имеются некоторые недоделки в плане условий (if). Для примера приведу код вычисления crc8:
unsigned char test(unsigned char *a, unsigned char size)
{
unsigned char crc = 0xFF;
unsigned char i;
while (size--)
{
crc ^= *a++;
for (i = 0; i < 8; i++)
crc = crc & 0x80 ? (crc<<1) ^ 0x31 : crc<<1;
}
return crc;
}
А вот что мы получаем на выходе (входные данные передаются через HL - указатель на массив, A - длина массива):
; BB#0: ; %entry
ld b, a
ld c, -1
jp .BB0_3
.BB0_2: ; %while.cond.loopexit
; in Loop: Header=BB0_3 Depth=1
inc hl
.BB0_3: ; %while.cond.loopexit
; =>This Loop Header: Depth=1
; Child Loop BB0_5 Depth 2
ld a, b
cp 0
jp z, .BB0_1
; BB#4: ; %while.body
; in Loop: Header=BB0_3 Depth=1
ld a, (hl)
xor c
ld e, a
ld d, 0
dec b
.BB0_5: ; %for.body
; Parent Loop BB0_3 Depth=1
; => This Inner Loop Header: Depth=2
ld a, e
and -128
ld hx, a
sla e
ld a, e
xor 49
ld c, a
ld a, hx
cp 0
jp nz, .BB0_7
; BB#6: ; %for.body
; in Loop: Header=BB0_5 Depth=2
ld c, e
.BB0_7: ; %for.body
; in Loop: Header=BB0_5 Depth=2
inc d
ld a, d
cp 8
ld e, c
jp z, .BB0_2
jp .BB0_5
.BB0_1: ; %while.end
ld a, c
ret
Конечно не всё идеально, но crc8 вычисляется правильно. Если у кого-то есть свои примеры и интересно во что они скомпилируются, кидайте, покажу. Но замечу еще раз, что пока работа полностью реализована только для 8 битной арифметики, т.е. int пока в вычислениях использовать не получится. В ближайшее время займусь как раз реализацией 16 битной арифметики. Пока из нового все. :)
shurik-ua
22.11.2012, 02:12
Интересный проект.
Интересно как будет выглядеть декодер mp3 в Z80 асме ).
P.S. а х86 фронт-энд вы случайно не планируете сделать ) ?
shurik-ua, front-end'ов для llvm существует уже не мало. Начиная от clang или gcc+DragonEgg заканчивая Lua.
Уже результат. Таки инты надо)
Пока нет времени решил приостановить ненадолго проект. После выхода LLVM 3.2 планирую продолжить работу перейдя на новую ветку.
PS По данным с офф. сайта дата релиза LLVM 3.2 планируется 16 декабря.
Пока нет времени решил приостановить ненадолго проект. После выхода LLVM 3.2 планирую продолжить работу перейдя на новую ветку.
PS По данным с офф. сайта дата релиза LLVM 3.2 планируется 16 декабря.
хорошо. до января отложил?
Нет, хотя релиз всё еще не вышел. Я решил перейти на использование trunk ветки разработки llvm и перешел с subversion на git. Т.е. сейчас уже работаю дальше)) Чуть позже будет ссылка на репозиторий на github'е.
Error404
18.02.2013, 20:05
Как продвигается проект?
За прошедшие пару месяцев я перешел на разработку в trunk ветку LLVM. Часть кода была переписана, другая часть изменена. Была добавлена генерация машинного кода, который сейчас можно увидеть в ассемблерных листингах, а в дальнейшем будет возможность получать бинарные файлы. В целом сейчас имеются пожалуй два основных вопроса, которые нужно решить:
Реализовать инструкции для работа с памятью (чтение/запись данных). Здесь основная проблема сейчас в том, что у Z80 есть много команд для чтения/записи данных, но сами опкоды имеют разный формат и при этом они еще используют лишь определенные регистры. Например команды чтения:
LD A,(BC/DE)
LD r,(HL)
LD r,(IX/IY+offset)
LD A,(nn)
LD HL,(nn)
LD rp,(nn)
Тут с точки зрения простоты реализации для LLVM лучше всего было бы иметь команды вида LD rp,(nn), т.е. где нет явно определенных операндов.
Как использовать альтернативный набор регистров и стоит ли это делать вообще.
В остальном уже сейчас можно получать рабочий код, правда на данный момент он будет местами громоздкий и не эффективный, но это уже дело оптимизации, которая будет следующим этапом.
Весь проект находится здесь: https://github.com/earl1k/llvm-z80
mastermind
19.02.2013, 18:48
По поводу альтернативных регистров я как-то ломал голову. Идеи такие (в идеале нужно как-то что-то из этого комбинировать наверное):
1. Определить альтернативные регистры наряду с обычными и потом специальным проходом(-ами) расставлять ΕΧΧ и т.п., разруливать использование регистров из разных наборов в одной операции, группировать операции по набору используемых регистров (возможно имеющийся в llvm механизм scheduling как-то можно для этого заюзать) и пр. Что-то подобное, если я правильно помню, сделано в X86-бэкенде для FP-операций.
2. Разные наборы регистров - в разных классах, соответственно разные наборы [идентичных] операций для них. (псевдоинструкции пользующие альт. регистры, которые впоследствии преобразуются в обычные) Громоздко или/и нужно дублирующие операции автогенерировать. Проблема расстановки ΕΧΧ и т.п., группировки операций для минимизации кол-ва ΕΧΧ также остается.
3. Псевдо-32-бит регистры, (HLHL и т.д.) см. например примеры в псевдокоде на http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html
4. Некий вариант п. 1 или 2 с собственным register allocator, учитывающим наличие двух наборов.
Собственно, задачи правильной расстановки операций ΕΧΧ и группировки регистровых операций для минимизации их кол-ва, необходимо решать в любом случае.
mastermind, я тоже обдумывал разные варианты. Мне как раз кажется наиболее лучшим вариантом будет 1-ый или 3-ий вариант, который ты описал.
Для 1-го случая надо разобраться получше как работает scheduling, думаю он поможет группировать команды таким образом, чтобы минимизировать кол-во переключения наборов регистров через EXX.
Для реализации 3-го варианта достаточно просто добавить псевдоинструкции и потом их вручную разворачивать.
mastermind
19.02.2013, 19:15
Кстати, насчет rtlib, вот это видел?: http://compiler-rt.llvm.org/
The current feature set of compiler-rt is:
Full support for the libgcc interfaces on supported targets.
High performance hand tuned implementations of commonly used functions like __floatundidf in assembly that are dramatically faster than the libgcc implementations.
A target-independent implementation of the Apple "Blocks" runtime interfaces.
Вроде оно в целом на C написано с вариациями оптимизированного кода на асме для разных процессоров. Т.е. по идее для начала можно тупо заюзать и для Z80, оптимизациями можно и позже заняться.
О libgcc:
http://gcc.gnu.org/onlinedocs/gccint/Libgcc.html
Most of the routines in libgcc handle arithmetic operations that the target processor cannot perform directly. This includes integer multiply and divide on some machines, and all floating-point and fixed-point operations on other machines. libgcc also includes routines for exception handling, and a handful of miscellaneous operations.
Т.е. софтовая реализация умножений, делений, fp-операций и т.п.
mastermind, это уже видел) Прикрутим обязательно и посмотрим что выйдет
Рад, что тема еще не забыта. Работа над проектом идет, правда в последнее время совсем нет времени на него. :(
Вот по этой ссылочке есть примерный roadmap с указанием того, что уже реализовано и что планируется реализовать в ближайшее время: https://github.com/earl1k/llvm-z80/wiki/Roadmap
Поддерживаю. Пора обновляться и смотреть что нового добавилось и изменилось в LLVM, ведь в прошлом месяце вышла новая версия этой замечательной системы. :) Вот только возьмусь за это где-то в конце августа-сентябре, т.к. через неделю отпуск и на работе надо дела кое-какие утрясти.
Вернулся из отпуска. Обновил репозиторий объединив имеющиеся наработки со свежей веткой LLVM.
Репозиторий: https://github.com/earl1k/llvm-z80
Рад, что тема еще не забыта. Работа над проектом идет, правда в последнее время совсем нет времени на него. :(
Вот по этой ссылочке есть примерный roadmap с указанием того, что уже реализовано и что планируется реализовать в ближайшее время: https://github.com/earl1k/llvm-z80/wiki/Roadmap
Планы наполеоновские, приятно почитать. А как сделаешь - тебя на руках будут носить)
С другой стороны можно вместо HL использовать IX, тогда обращение будет выглядеть более приятно:
ld e,(ix+offset)
ld d,(ix+offset+1)
6 байт и всего две команды, но тут у нас выделение и освобождение памяти на стеке будет чуть больше размером, т.к. работать будем с IX.
Если предположить, что наша программа будет находится в озу, то можно придти к такому варианту выделения памяти:
ld (.stack),sp
ld hl,-size
add hl,sp
ld sp,hl
...
ld sp,0
.stack $-2
ret
Дополнительно к этому можно добавить опцию, указывающую будет ли программа работать в озу или пзу, чтобы генерировать нужный код.
Собственно вопросов всего три:
1) Как лучше выделять память на стеке? Какой регистр использовать - HL или IX?
2) Как обращаться к переменным на стеке
3) Использовать лучше "честный" метод или сделать возможность включения более быстрого?
Если еще есть какие-то замечения и/или пожелания по данному вопросу, буду рад выслушать.
Немного поздновато, конечно. :) Но, тем не менее.
Сам столкнулся вчера с такой проблемой:
Да, во-первых, данные со стэка медленно забираются, а во-вторых, если в процедуре много работы с ними (особенно в циклах), то программа из за косвенной адресации через индексные регистры замедляется чрезвычайно! Ведь любая операция съедает 19 тактов, и это на 1 байт данных(!).
Решил проблему так:
взял вместо Си-исходника, сгенерённый SDCC asm-исходник. Добавил в конце п/п пустые переменные, соответствующие аргументам функции. В начале процедуры добавил копирование аргументов со стека через LDI/LDIR.
Соответственно, вручную пришлось править весь исходник, но оно того стоило: теперь работает в разы быстрее.
Думаю также с локальными переменными поступить.
Sergey, на данный момент генерируется код, работающий со стеком через индексные регистры. В последствии можно будет сделать опцию и добавить какой-нибудь другой алгоритм передачи параметров.
В описанном тобой способе есть свои минусы. Например не получится реализовать рекурсивные функции, однако, в некоторых случаях это приемлемо и даст хороший прирост скорости:)
Sergey, на данный момент генерируется код, работающий со стеком через индексные регистры. В последствии можно будет сделать опцию и добавить какой-нибудь другой алгоритм передачи параметров.
В описанном тобой способе есть свои минусы. Например не получится реализовать рекурсивные функции, однако, в некоторых случаях это приемлемо и даст хороший прирост скорости:)
Это понятно. Также ещё этот метод не подойдёт для ПЗУ.
Думаю, опцию можно реализовать как "__naked" в SDCC.
Кстати, если что, готов в бетатестеры.
---------- Post added at 16:07 ---------- Previous post was at 16:00 ----------
Ах да, чуть не забыл: реквестирую инструкцию препроцессора для выравнивания кода/данных на границу слова. В SDCC такой фичи очень не хватает, ведь DMA в TS-Config работает только с четными адресами.
вчера скачал всё репозитории.
llvm собрался, а вот clang не захотел :(
вчера скачал всё репозитории.
llvm собрался, а вот clang не захотел :(
Собирал с помощью скрипта ./configure или cmake?
Собирал с помощью скрипта ./configure или cmake?
cmake
Странно. У меня с помощью cmake все собирается. Проблемы могут быть как раз только при использовании ../configure.
Нужен лог сборки, чтобы понять в чем проблема.
Можешь написать мне на почту или в аську, разберемся.
сейчас не могу и когда смогу не знаю. наверное я гдето сам прокосячил. но если доберусь - напишу.
В общем поглядел.
Собираю llvm:
# cmake -DCMAKE_INSTALL_PREFIX=/home/salex/app-clang-z80/ ..
# make -j6
# make install
Всё пучком, инсталлирует.
Далее собираю clang
#cmake -DCLANG_PATH_TO_LLVM_BUILD=/home/salex/app-clang-z80/ -DCMAKE_INSTALL_PREFIX=/home/salex/app-clang-z80/ ..
# make
Получаю:
make[2]: *** Нет правила для сборки цели `/home/salex/app-clang-z80/bin/llvm-tblgen', требуемой для `include/clang/Driver/Options.inc.tmp'. Останов.
make[1]: *** [include/clang/Driver/CMakeFiles/ClangDriverOptions.dir/all] Ошибка 2
make: *** [all] Ошибка 2
И ещё - многопоточная сборка для сlang вообще не работает почемуто. (Ключ make -j6)
Подозреваю, что clang и llvm находятся в разных папках. Если это так, то советую сделать следующим образом.
1. Качаем llvm и кладем например в папку llvm-z80
2. Качаем clang и кладем в папку llvm-z80/tools/clang
3. Натравливаем cmake на llvm-z80
4. Запускаем сборку командой make и ждем.
Кстати собирать лучше не в папке где находятся исходники llvm/clang, а в отдельной.
Подозреваю, что clang и llvm находятся в разных папках. Если это так, то советую сделать следующим образом.
2. Качаем clang и кладем в папку llvm-z80/tools/clang
О!! ПЛЯ! я и не знал! ЩАЗ проверю
Кстати собирать лучше не в папке где находятся исходники llvm/clang, а в отдельной.
Это основная идея cmake. Каждый день на работе им пользуюсь. :)
---------- Post added at 21:36 ---------- Previous post was at 21:13 ----------
УРЯЯЯ! Собрался. Сейчас пытать его буду)
---------- Post added at 22:15 ---------- Previous post was at 21:36 ----------
В общем - даже C++ компилирует)
Но оптимизации пока что никакой.
Помимо оптимизации там пока еще имеются некоторые непонятности, как те или иные вещи реализовать. Например для временного хранения компилятор умеет перекладывать значения в регистр IY и загружать обратно посредством push/pop, но вот когда регистров становится недостаточно и возникает необходимость сохранять значения на стеке, то возникают оказии в виде попытки сгенерировать команду:
LD (IX+NN),HY/LY Запретить сохранять на стек только регистр IY нельзя, т.к. аллокация регистров идет после генерации кода. Во всяком случае как это сделать я пока не понял, но уверен, что это реально. Можно просто запретить использовать IY для временного хранения значений, но тогда компилятор начнет чаще в стек лазить за значениями. В общем, ситуация не очень приятная. Это то, на чем сейчас все остановилось. В остальном количество команд, которые компилятор умеет генерировать уже почти хватает для всех видов конструкций, но код как ты заметил не самый оптимальный.
Это то, на чем сейчас все остановилось. В остальном количество команд, которые компилятор умеет генерировать уже почти хватает для всех видов конструкций, но код как ты заметил не самый оптимальный.
Да бог с ним, что "неоптимальный". Дело наживное. Главное, что это вообще сделано!
А z80-ассемблер ты не пытался прикрутить ещё?
Еще не делал. Однако это позволит делать inline подстановки на асме прямо в код Си. Можно даже дизассемблер туда пристроить:)
Ebuild случаем никто не готовил из llvm-z80 для Gentoo ?
Нет. Я думал сперва довести все же дело до более менее рабочего результата. Тогда и запилю.
Еще не делал. Однако это позволит делать inline подстановки на асме прямо в код Си. Можно даже дизассемблер туда пристроить:)
насколько я знаю есть binutils с поддержкой z80
сейчас буду искать
---------- Post added at 20:56 ---------- Previous post was at 19:55 ----------
В общем так.
1. Собрал binutils-2.22.tar.bz2
./configure --prefix=$PREFIX --target=z80-unknown-coff --disable-static --with-gnu-ld --with-gnu-as --enable-install-libbfd --disable-werror
(Опции нагло взяты из дебианоского скрипта)
2. Попытался откомпилировать программу на ассемблере, полученную clang'ом.
НИ ФИГА НЕ ВЫШЛО!
Причина: Неверно работает кодогенератор:
Исходный код.
int a;
int b;
int c;
int main(int argc, char* argv[]){
c = a+b;
return(0);
}
Полученный ассемблер (оптимизация -Os)
.file "test.c"
.text
.globl main
.type main,@function
main:
push ix
push de
push bc
ld ix, 0
add ix, sp
ld sp, ix
ld hl, b
inc hl
ld b, (hl)
ld a, (b)
ld c, a
ld hl, a
inc hl
ld d, (hl)
ld a, (a)
ld e, a
ld h, b
ld l, c
add hl, de
ld b, h
ld c, l
ld hl, c
inc hl
ld (hl), b
ld a, c
ld (c), a
ld hl, 0
pop bc
pop de
pop ix
ret
.tmp0:
.size main, .tmp0-main
.type a,@object
.comm a,2,1
.type b,@object
.comm b,2,1
.type c,@object
.comm c,2,1
Обрати внимание на команды:
ld hl, b
...
ld hl, a
...
ld a, (a)
Без оптимизации картина та же.
Надо бы привести генерируемые команды к стандарту, пониемому бинутилсами.
тогда будем тестить дальше, уже на реале.
насколько я знаю есть binutils с поддержкой z80
сейчас буду искать
Их z80-coff очень ограниченная штука, кстати, в таблице релоцирования COFF нет некоторых ключевых фич. Я как-то пытался его прикрутить к sdcc, и споткнулся на том, что вот такое (взятие старшего и младшего байта адреса, который неизвестен на этапе компиляции)
section .data
DATA
defm "Hello, world"
section .text
LD H, DATA >> 8
LD L, DATA & 0xFF
там в принципе невозможно. sdcc же такое генерит направо и налево, да и практически все прочие z80-ассемблеры понимают подобные по смыслу конструкции. Нужен ELF с его complex relocations.
Обрати внимание на команды:
ld hl, b
...
ld hl, a
...
ld a, (a)
Без оптимизации картина та же.
Надо бы привести генерируемые команды к стандарту, пониемому бинутилсами.
тогда будем тестить дальше, уже на реале.
Это все потому, что приводить к какому-то виду нужно как минимум рабочий вариант, а сейчас некоторые конструкции мой бэкенд еще не умеет строить. По этой причине генерацию листинга делал просто для более наглядной проверки получаемого результата и отладки.
В llvm есть свои инструменты подобные binutils. Думал в перспективе их использовать.
Альтернативы ему нет ?
Есть. Использовать DragonEgg+gcc для генерация llvm кода. Сам еще не пробовал. Очень интересно что получится:)
Их z80-coff очень ограниченная штука, кстати, в таблице релоцирования COFF нет некоторых ключевых фич. Я как-то пытался его прикрутить к sdcc, и споткнулся на том, что вот такое (взятие старшего и младшего байта адреса, который неизвестен на этапе компиляции)
section .data
DATA
defm "Hello, world"
section .text
LD H, DATA >> 8
LD L, DATA & 0xFF
там в принципе невозможно. sdcc же такое генерит направо и налево, да и практически все прочие z80-ассемблеры понимают подобные по смыслу конструкции. Нужен ELF с его complex relocations.
будем дальше копать. В принципе асм можно любой прикрутить.
---------- Post added at 22:27 ---------- Previous post was at 22:24 ----------
Есть. Использовать DragonEgg+gcc для генерация llvm кода. Сам еще не пробовал. Очень интересно что получится:)
Я, конечно, наглею, давая советы - но может лучше сначала кодогенератор допилишь до того, чтобы он неоптимальный, но рабочий код давал со стандартными опкодами, а потом уж яйца дракона кушать будешь? :p
Автор жив, проект спит. Надеюсь это временно, т.к. самому он интересен. В данный момент работаю над другими проектами, т.к. с llvm:
1. Не нашел больше людей, кто мог бы помочь с разработкой, а для одного все таки долго/сложно его развивать.
2. LLVM активно развивается и нужно успевать следить за всеми новыми возможностями, на что не хватает времени.
mastermind
13.12.2014, 13:52
Зашел глянуть на github происходит ли что-нибудь с проектом.
Месяц назад некто Seth Traverse (https://github.com/WinMac32) склонировал проект EARL'а и добавил пару коммитов.
См. https://github.com/WinMac32/llvm-z80/commits/z80
Initial implementation of inline asm …
WinMac32 authored on Nov 2
Based heavily (almost completely) off of the "CPU0" implementation
with modifications to make it output correct z80 operands
Next step is testing to see what CPU0-specific features need to be
changed, or removed, so that it works in all use cases.
We now output "correct" z80 syntax. …
WinMac32 authored on Nov 6
Also temp fix on some C programs not compiling...
It seems LLVM sometimes wants to put HL onto the stack, but it first
moves it to IY (seriously wtf) and then tries to move IY to memory
indirectly using the IX register. Originally (and correctly, I
suppose), it would crash here.
However, I'm temporarily allowing it for testing. This isn't an
instruction, and it should't even be using IY for stuff like that
in the first place. LLVM needs to be stopped from getting to this
point.
(склонировал он, правда, как-то некорректно, github не показывает связь с проектом EARL'a)
---------- Post added at 12:52 ---------- Previous post was at 12:49 ----------
Упомянутый у него CPU0 - некий китайский процессор (выдуманный что ли для этой книги подобно MIX Asm Дональда Кнута?), процесс создания LLVM-бэкенда для которого подробнейшим образом описан здесь: http://jonathan2251.github.io/lbd/ (онлайн + epub и pdf книжки) - выглядит как отличное руководство по созданию бэкендов. (пусть и на ужасно ломаном англ. языке)
Да, я уже видел этот репозиторий на github'е и книжку про CPU0. Есть там некоторые интересные моменты.
Помимо всего, я недавно отписывался на одном из иностранных форумах, где тоже интересовались моим проектом (http://www.cemetech.net/forum/viewtopic.php?p=226619#226619). Сейчас у меня снова появился интерес к проекту, но завал на работе не позволяет заняться им.:(
mastermind
09.03.2016, 20:56
(перевод гуглом с французского, немного подправленный):
jacobly, крупный контрибьютор в CEmu (https://github.com/CE-Programming/CEmu), начал переделывать бэкэнд Z80 + eZ80 для LLVM, с нуля. Сейчас этот бэкенд начинает генерировать код для простых случаев, он опубликовал свою работу, чтобы другие могли внести свой вклад и улучшить, в духе обмена нашего сообщества, на данный момент есть еще много работы, но это важный шаг, и это наша коллективная ответственность содействовать тем, кто имеет навыки и время, а также воспользоваться, когда он будет работать хорошо.
Репозиторий проекта: https://github.com/jacobly0/llvm-z80
Источник информации: IRC (на EFNet, канал # eZ80-DEV)
Отсюда (фрагмент сообщения от 3 марта 2016 г.): https://tiplanet.org/forum/viewtopic.php?t=18038&p=197788
Хорошая новость. Буду следить за этим проектом. Жаль сейчас времени свободного почти нет, но если появится, попробую помочь ему.
Т.к. Z80 8-битный процессор, то int должен иметь размер 1 байт или я не прав?
Стандарт языка Си гарантирует, что char <= short <= int <= long.
В вашем случае char = 1 байт, short = int = 2 байта, long на ваше усмотрение - 2-4 байта.
склоняюсь к long=4 байта, а long long = 8 байт. Чтобы не выкоблучиваться, если надо больше 16 бит.
На спеке это редко надо, но надо.
склоняюсь к long=4 байта, а long long = 8 байт. Чтобы не выкоблучиваться, если надо больше 16 бит.
Тогда для этого хватит двух байт :)
На спеке это редко надо, но надо.
Тот, кому такое нужно, сможет сделать это на массиве байт. Когда мне нужно хранить набор бит/состояний, то я использую массив uint32_t (и это работает на любых современных архитектурах), но с тем же успехом (и без потери производительности) можно использовать и массив uint8_t.
Стандарт языка Си гарантирует, что char <= short <= int <= long.
В вашем случае char = 1 байт, short = int = 2 байта, long на ваше усмотрение - 2-4 байта.
Я, конечно, не программер. Но, по-моему, из приведённого выражения следует только то, что int не может быть больше long, а short - не больше int. Т.е. int может быть 4 байта, а long 2 байта - НЕТ.
Массив байт - для арифметики как-то не очень.
Barmaley_m
10.05.2016, 22:25
Современные каноны программирования на C, особенно для встраиваемых систем, отказываются от традиционных типов int, short и long, и рекомендуют использовать только заданные точно типы: int8_t, int16_t, int32_t, int64_t и их беззнаковые аналоги. Так что нет смысла здесь копья ломать. Пишите так, чтобы программа не зависела от ширины int, short и long на конкретной платформе.
Собственно, откуда взялась эта идущая родом из истоков языка C неточная спецификация на ширину целых чисел? В те времена считалось, что пусть программист укажет, что желает работать с таким целым типом, который наиболее удобно представим на целевой платформе. Компилятор же решит, какая у этого типа будет ширина. Но на практике оказалось, что программисты пишут программы исходя не из наихудшего случая (т.е., к примеру, не допускать, чтобы переменные int выходили за диапазон -32768...32767, так как на некоторых компьютерах int могут иметь ширину 16 бит), а из той ширины типов, которая принята на платформе, под которой работает программист. И в конце концов вместо улучшения портируемости программ произошло ее ухудшение.
В самом деле, пусть уж лучше портированная программа работает медленно, но правильно, чем сбивается или того хуже - чтобы в ней из-за переполнений образовывались дыры безопасности.
Ситуация усугублялась несовместимостью библиотек и причудами компиляторов. В начале 2000х я пытался писать программы на C и C++ исходя из "наихудшего случая - 16 бит на int". Там, где были нужны 32-битовые переменные - использовал long. Но однажды мне пришлось компилировать свои программы на другой платформе, а там был принят стандарт: long = 64 бита. В результате съехали все размеры структур, и программа не смогла работать с двоичными файлами, с которыми она должна была работать. Так что даже программирование под "наихудший случай" не позволяет избегать проблем с портируемостью кода на С; более того, в данном конкретном случае мои усилия оказались даже вредными. Использовал бы int - все работало бы нормально.
Так что теперь я перешел на типы фиксированной длины, разве что, кроме случаев, когда какая-нибудь библиотека или API требует старого традиционного типа. Чего и остальным советую.
mastermind
23.08.2016, 03:26
Записываю сюда чтобы не забылось:
В последней версии Rust (1.11) добавлена поддержка 16-битных указателей: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1110-2016-08-18
rustc support 16-bit pointer sizes. (https://github.com/rust-lang/rust/pull/33460) No targets use this yet, but it works toward AVR support.
(имеет отношение к теме; rust - "низкоуровневый" язык использующий LLVM для компиляции)
OrionExt
25.08.2016, 04:20
Не взлетит. Только на одн... Держится. Без мотивации
Error404
21.03.2017, 15:09
Записываю сюда чтобы не забылось:
В последней версии Rust (1.11) добавлена поддержка 16-битных указателей: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1110-2016-08-18
(имеет отношение к теме; rust - "низкоуровневый" язык использующий LLVM для компиляции)
Вот тут пишут, что есть работоспособная реализация LLVM для Z80 (https://github.com/earl1k/llvm-z80), поддерживается в том числе и 32bit int. Статья с примерами компиляции (используют clang):
С-систаксис: https://olduino.wordpress.com/2014/12/30/llvm-for-the-z80-another-source-of-inspiration/
С++: https://olduino.wordpress.com/2015/03/19/wow-this-is-why-i-want-an-llvm-compiler/
Это кто то из наших таки добил гадину? И вообще, пробовал ли кто-то с помощью этого варианта LLVM собрать что-то приличное?
shurik-ua
21.03.2017, 16:04
Судя по нику топикстартера - по ссылке это как раз его наработки. Просто здесь движуха отсутствует напрочь.
Error404
21.03.2017, 16:28
Судя по нику топикстартера - по ссылке это как раз его наработки. Просто здесь движуха отсутствует напрочь.
Выглядит похоже. Хоть бы автор отписался что да как, съэкономил нам время на исследования методом проб и ошибок. :)
Shadow Maker
21.03.2017, 16:39
У EARL последний коммит в 2013 году, о чем вы говорите.
- - - Добавлено - - -
А так вообще вот чувак более живой, пилит что-то: https://github.com/jacobly0/llvm-z80
Вот тут пишут, что есть работоспособная реализация LLVM для Z80 (https://github.com/earl1k/llvm-z80), поддерживается в том числе и 32bit int. Статья с примерами компиляции (используют clang):
С-систаксис: https://olduino.wordpress.com/2014/12/30/llvm-for-the-z80-another-source-of-inspiration/
С++: https://olduino.wordpress.com/2015/03/19/wow-this-is-why-i-want-an-llvm-compiler/
Это кто то из наших таки добил гадину? И вообще, пробовал ли кто-то с помощью этого варианта LLVM собрать что-то приличное?
Я так и не доделал все до конца в связи с отсутствием времени и некоторыми проблемами в понимании как реализовать нужные мне вещи в LLVM. Примеры по ссылкам выдают как раз тот код, который и у меня был, т.е. вряд ли там кто-то что-то еще допиливал. Могу заверить, что некоторые конструкции просто не скомпилируются и поэтому проект неработоспособный.
А так вообще вот чувак более живой, пилит что-то: https://github.com/jacobly0/llvm-z80
Да, у этого человека в настоящий момент идет понемногу движуха, но я не пробовал тестировать его код.
Error404
15.06.2018, 16:28
Сообщение от Shadow Maker Посмотреть сообщение
А так вообще вот чувак более живой, пилит что-то: https://github.com/jacobly0/llvm-z80
Да, у этого человека в настоящий момент идет понемногу движуха, но я не пробовал тестировать его код.
Судя по данной ветке тамошнего форума:
https://tiplanet.org/forum/viewtopic.php?t=18038&p=197788
используя реализацию от jacobly, на CLang получилось собирать достаточно сложные проекты. Я правда не понял там таргет Z80 или ez80?
Последний коммит 26 Oct 2017.
Хотелось бы мнения от интересующихся.
Вот тут упоминаются два таргета, z80 и ez80.
https://github.com/jacobly0/llvm-z80/blob/6380335117ebddf11b72876b68753e31c5d185da/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp
На форуме третье сообщение снизу с полными инструкциями для сборки от печки. Проще попробовать?
mastermind
01.05.2019, 18:44
Еще две попытки создания LLVM-бэкэнда:
1. https://github.com/Bevinsky/llvm-gbz80 (GBZ80 - урезанная версия Z80 для GameBoy)
* Инструкция по сборке: https://github.com/Bevinsky/llvm-gbz80/wiki/Basic-build-instructions
* Dockerfile для сборки этого проекта в docker: https://github.com/dfrankland/docker-gameboy
2. https://github.com/MI-CHI/llvm-z80-backend
NEO SPECTRUMAN
01.05.2019, 18:55
вопрос
даже если оно кто то допишет
чего это нам такого даст?
вопрос
даже если оно кто то допишет
чего это нам такого даст?
а што ты ждеш?
NEO SPECTRUMAN
01.05.2019, 20:07
а што ты ждеш?
Я ничего не жду
и уверен что пишется просто ради писания и ради того чтоб было
а спрашиваю можот кто то чего то и ждет и видит этому применение
Я ничего не жду
и уверен что пишется просто ради писания и ради того чтоб было
а спрашиваю можот кто то чего то и ждет и видит этому применение
мне показалось, что ворчиш(:
по идее берешь raze.dll, лепишь к нему немножко кода и получаешь эмуляцию. С таймингом не разбирался.
mastermind
01.05.2019, 20:40
Я ничего не жду
и уверен что пишется просто ради писания и ради того чтоб было
а спрашиваю можот кто то чего то и ждет и видит этому применение
Те кто пишут, дорабатывают сразу же clang заодно. Т.е. хотят как минимум этот C/C++ компилятор использовать. Можно еще, например, Rust доработать. Еще Carp, возможно, хорошо бы лег на 8-битные платформы ( https://github.com/carp-lang/Carp ) - он генерирует C.
Я как-то находил инструкции по сборке LLVM clang для z80. Я там мало что понял. Но там, похоже, получаются таргеты для z80 и ez80. Собирается все очень долго и муторно. Чтобы второй раз все не искать, собрал все в одном месте и завернул в образ на Dockerhub. Так что теперь собирать самому не надо (весь день). Весит 1.3 Гб примерно.
https://github.com/UA3MQJ/z80-llvm
https://hub.docker.com/repository/docker/ua3mqj/z80llvm
На компьютере должен быть установлен Docker. Вроде бы можно даже на Windows, но я не пробовал.
Кладем в текущую папку файл, запускаем и попадаем в среду контейнера, где доступен clang
docker run --rm -it -v$(pwd):/current_dir ua3mqj/z80llvm:1.0.3 bash
Компилируем
clang -cc1 -triple z80 -S -O3 -o test1.s test1.c
Из такой функции
char foo(char a, char b) {
return a + b;
}
получаем такой выхлоп
SEGMENT CODE
.file "test1.c"
XDEF _foo
_foo:
push ix
ld ix, 0
add ix, sp
ld a, (ix + 6)
add a, (ix + 4)
pop ix
ret
Далее не пока не экспериментировал.
Oleg N. Cher
04.11.2020, 13:12
Меня заинтересовало, что кодогенератор Clang разворачивает рекурсию в цикл. Это реально любопытно. Смотрите (в репе) пример с факториалом.
Сюда бы ещё асм-вставки и регистровую модель передачи параметров... а может есть?)
Bedazzle
04.11.2020, 13:49
На компьютере должен быть установлен Docker. Вроде бы можно даже на Windows, но я не пробовал.
На работе из-под винды как-то так
https://i.imgur.com/J7HK3E4.png
mastermind
27.07.2022, 03:14
Для 6502 сделали умельцы работоспособный, оптимизирующий, backend: https://llvm-mos.org/
C, C++, Rust можно использовать, с некоторыми понятными ограничениями.
Видео:
https://www.youtube.com/watch?v=2lW3WHPtmKo
Меня заинтересовало, что кодогенератор Clang разворачивает рекурсию в цикл.
Разворот концевой рекурсии в цикл -- стандарт для компиляторов уже много десятилетий. Собственно, многие современные алгоритмы обработки списков полагаются на это.
Сюда бы ещё асм-вставки и регистровую модель передачи параметров... а может есть?)
Про это Clang ничего знать не должен, у него другие задачи. А так, у компиляторов на основе Clang всё это, разумеется, есть. Как же иначе?
mastermind
27.07.2022, 18:14
Для 6502 сделали умельцы работоспособный, оптимизирующий, backend: https://llvm-mos.org/
C, C++, Rust можно использовать, с некоторыми понятными ограничениями.
Вот тут тема про этот backend: http://forum.6502.org/viewtopic.php?f=2&t=6450
Oleg N. Cher
21.01.2024, 07:59
Тема заглохла, но, возможно, кому-то будет интересен прямой (не через Си) способ разработки на Обероне под 6502 и Z80 (не проверен).
У Майкла Гроссниклауса (Michael Grossniklaus) есть компилятор Oberon, который использует LLVM в качестве бэкэнда и, по крайней мере, теоретически, должен быть способен компилироваться в любой системе, имеющей бэкэнд LLVM, но в настоящее время поддерживает разработку под Windowsx86/AMD64, MacOS и Linux, а поддержка Windows ARM находится в стадии разработки.
В этом проекте реализуется компилятор языка программирования Oberon в качестве интерфейса к инфраструктуре компилятора LLVM. Он написан на C++ и возник как проект, сопровождающий курс магистратуры «Конструирование компилятора», преподаваемый в Университете Констанца. Как следствие, этот компилятор изначально предназначался только для подмножества языка Оберон-0, как описано в книге Никлауса Вирта «Создание компилятора» (глава 6, стр. 30-32). С тех пор поддерживаемое подмножество Oberon постоянно расширялось с целью в конечном итоге охватить полную спецификацию языка, как описано в последней версии Oberon Language Report. Помимо этих «официальных» расширений, к поддерживаемому диалекту языка программирования Оберон были добавлены и другие функции. Эти функции были либо вдохновлены удобством, например, взаимодействием со стандартными библиотеками, либо ностальгией автора компилятора по изучению Turbo Pascal 6.0 как его первого языка программирования в старшей школе. Описание поддерживаемого в настоящее время диалекта Оберона с точки зрения синтаксиса и семантики (неофициальных функций) можно найти в разделе Wiki этого репозитория проекта.
Для macOS и Linux были протестированы версии x64 и Arm64. Полная поддержка Windows (arm64) в настоящее время находится в стадии разработки. Подробные инструкции по сборке компилятора Оберона и его зависимостей можно найти в разделе Wiki репозитория проекта.
https://github.com/zaskar9/oberon-lang
Powered by vBulletin® Version 4.2.5 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot