В последнее время занимался продумыванием и переписыванием .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 файлах пока думаю.
На этом пока все. Если у кого-то есть какие-то вопросы или пожелания, говорите.