AnyC скорее мёртв.
А вот библиотеки... Я думаю что у каждого будут свои, ибо пока нет нормальной ОСи, для которой их следовало бы делать.
Вид для печати
AnyC скорее мёртв.
А вот библиотеки... Я думаю что у каждого будут свои, ибо пока нет нормальной ОСи, для которой их следовало бы делать.
тоже еще проблемы %))Цитата:
Сообщение от andrews
был бы нормальный компилятор, за библиотеками дело не станет, готов лично поучаствовать :rolleyes:
это если разговор о нативном C для zx.
а если кросскомпилер, то чем z88dk не устраивает? с splib можно вполне сносные игрульки писать (спрайты, тайлы, прерывания, опрос клавы/джойстиков/мыши, работа с памятью... phantomas infinity на нем написан)... остальное прикручивается из asm-сорцов за 5 минут.
а сделан z88dk, кстати, на основе small C
Имхо, из открытых проектов z88dk по отношению качество кода/фичастость рулит на данный момент, но все понимают, особенно оглядываясь на hitech, что это не идеал :)
Идеал/не идеал... Где бенчмарки? =) Надо бы для такого тестирования сделать тест пакет.
Ну это, есть же Dhrystone, хотя им и процессоры тестят, но у нас получится на одном процессоре будут теститься компиляторы.
Без проблем конвертится в исходник с мнемоникой Z80.Цитата:
Сообщение от NovaStorm
Я его кстати выкладывал на форуме в составе ZXCPM, перекомпилировав для работы с адреса 6100h.
Собирал, но он очень громозкий и требует для работы даже в CP/M не менее 50 КБайт TPA.Цитата:
Сообщение от NovaStorm
Это ещё зачем?Цитата:
Сообщение от Error404
Как это будет работать на z80?
Боюсь от реалий спеки от оторван напрочь =) особенно по памяти.
Почемуто по поводу нужности float вопросов не возникало, хотя очень много вычислений можно проводит с фиксированной, а не плавающей запятой (что несравненно быстрее и проще). Вот для этих целей 32разрядное целое и нужно. Прекрасно хранится в паре регистров (равно как и float)Цитата:
Сообщение от captain cobalt
Нормально будет работать. А нужно это затем, чтобы не изобретать велосипеды, а брать готовый код с любых платформ. Есть готовые реализации, к примеру, TCP/IP или FAT32 на С, причем в минималистской форме (специально для 8-биток). Да и вообще, я часто натыкался на необходимость в 32 bit int, а перелопачивать и отлаживать код из-за того, что компилер не может - это сильно на любителя. Из CP/M-овских нативных С-компиляторов мне пока известны (пробовал) только 2 компилятора из всего множества, изначально (а не через дурацкие char-конверсии) поддерживающие приемлимый набор типов - этоЦитата:
Сообщение от captain cobalt
Hitec C и Mix C. Но и тут ложка дегтя: оба хранят либы в уникальных форматах, тогда как удобнее если бы это был rel/irl.
Bdsc, SmallC, C80, MsxC тоже пробовал - не понравились из-за их предельного аскетизма поддерживаемых типов.
Это все да, но код-то он продолжает генерить для i8080 и поддержка возможностей z80 не появится :) Кроме того, кажется bdsc сразу компилит в бинарный код, без ассемблерного текста, что не есть хорошо.Цитата:
Сообщение от caro
Вообще-то int в С для всех 8 и 16битных процов, какие я знаю - 16 бит, а не 32 (AVR, PIC, I196 и т.п.). Так зачем это менять ? Пользуйся 32х битным long int или 64битным long long int. Причем, только там, где это надо. Это оптимальнее.Цитата:
Сообщение от Error404
Я так понял, что никто и не предлагал менять, проблема в том, что в некоторых недокомпиляторах C тип long int отсутствует как класс или равен тоже 2-м байтам :)Цитата:
Сообщение от SfS
Про long long я вообще молчу :)
AFAIK хрена с два ты его найдешь. Насколько я понимаю, оного существовать не может, по крайней мере для классического z80. Может для eZ80 разве что... У gcc тоже есть свои ограничения - он ориентирован на 32-бит/64-бит архитектуры и Flat memory Model. На ZX нет ни того ни другого.Цитата:
Сообщение от Vitamin
Есть другие открытые компиляторы, в том числе и z80-targetted.
К тому же C для ZX был бы действительно интересен, если бы еще и работал на самом ZX (я сторонник нативных систем). А даже если и представить себе, что gcc неким образом существует на Спеке, то Hello world он компилировал бы несколько суток. gcc - слишком сложная система с использованием нескольких промежуточных представлений.
Шутите? :v2_biggr:Цитата:
Сообщение от SfS
Оффтоп: Встречает "новый русский" (HP) "старого русского" (СР):
НР: - Кака дела?
СР: - Да вот, не ел три дня...
НР: Ну, братан, надо же себя как-то заставлять....
:smile:
А если серьезно: рад бы воспользоваться, да не могу: не знает тип long (32bit) большинство 8080/Z80 С-компиляторов (по крайней мере нативных, не писюковых) . Естественно я имел ввиду long... Т.е. принципиальную возможность использовать нативный целочисленный тип 32bit.
нативного типа такого нет. а для нативных типов int(16) и char(8) определены только операции + и -. Все остальное (*/%) пишется ручками. Равно как и весь перечень операций для long(32) и float(32). Будет поддержка- будет и тип.Цитата:
Сообщение от Error404
Пишется все что угодно и без привязки к процессору. Причем тут аппаратная поддержка? Любая арифметика делается. Только заниматься этим должно ядро компилятора (это я имел ввиду под определением "нативные типы" - с терминологией беда :smile: ). Чтобы не изобретать в дополнительных библиотеках порнографию вида:Цитата:
Сообщение от Vitamin
typedef LONG char*
void plus32(LONG op1, op2, result)
{}
...
char[4] a,b,c;
...
plus32(a,b,c); /* c=a+b */
А этим занимаются почти все компиляторы для 8080/Z80 - сделают 3 типа, а остальное - "сделай сам". Понятно, что при таком подходе никакие сторонние исходники не используешь (везде принято писать c=a+b , а не plus32(a,b,c) ), да и тормозит такая "прикрученная сбоку" арифметика жутко.
У авторов Hitec C в CP/M-версии компилятора, к примеру, хватило ресурсов реализовать все наиболее часто требуемые типы на все том же z80. Пока это лучшеее, что я видел из бесплатного (считая и PC-версии) и, похоже, единственное более-менее пригодное к употреблению.
Насколько С++ шаг вперед по отношению к С (class::operator ...), настолько и эти компилеры шаг назад от того же С...Цитата:
Сообщение от Error404
В нормальных компилерах есть runtime-библиотека, где определены функции типа __inttofloat __add32 etc, и компилятор занимается их вызовом. Т.е. обеспечиваются все нативные (для языка) типы, причем глубоко пофиг какой разрядности целевая машина.
Грустно, что такого не сделали...
"Комментарии в теле цикла замедляют его работу".Цитата:
Сообщение от Vitamin
А ты не знал? Низачот.
Я в принципе и сейчас бы помахал. В асме реализуются зачастую концепции недоступные в недоязычках вроде паскаля. Или C. Хотя в последнем не всё так уж и плохо, по сравнению с некоторыми... Тоже прикручиваются внешние макропроцессоры. Или можно потихоньку мигрировать в сторону C-два-креста.Цитата:
Я тоже когда-то бегал и махал флагом "асм рулит, С для ленивых". Но
Java. Там этих страшных указателей нет. И выделение памяти абсолютно-безопасносное (пока OOM killer не отстрелит).Цитата:
Так что для промышленного программирования лучше С пока ничего не придумали.
Да нет, самое главное конечно не это. Самое главное -- что
Java обучаются даже бабуины. Вся суть в этом.
www.htsoft.com? Никто не отменял. Только Z80 супротив современных RISC цпу, даже самых мелких мелкоконтроллеров,Цитата:
ЗЫ. Все-таки на спеке не хватает С с приличной средой разработки...
сильно проигрывает. В том смысле, на него C с его парадигмой распределения памяти хреновенько ложится.
Ага, DPTR. Мне уже второй год страшно в листинги из-за этого заглядывать. Вот я и не заглядываю. В small model оно, к слову, гораздо веселее.Цитата:
Сообщение от Vitamin
Несколько лет назад в инете искалось и у меня сохранился патч на GCC старой (2.x, вроде 2.95 примерно) версии GCC. Другое дело, что тут очень много зависит от:Цитата:
Кто-нибудь находил версию GCC для зетника? А то перерыл дофига ссылок- одни огрызки информации, ни сорцов ни бинарника. А то была
1) кодогенерирующего backend'а. А он явно плох.
2) стиля кодирования. Он от писишного должен отличаться.
Я бы сказал, на 50% возможность использования компилятора определяется программистом. И на 50% компилятором. Компилятор -- повторяюсь -- hitech. Лучше для Z80 (и PIC) нет.
small C compiler.Цитата:
маза собрать его же исходники для спека :) Ибо писать с нуля- весьма ресурсозатратно...
Если они такие серьёзные, то чего ж они изобретательством велосипедов занимаются. Этот велосипед называется PL/M и известен уже лет чуть ли не 30 как. Для Z80, кстати, должен быть и должен пускаться с лёгкостью в CP/M. Т.е. на пентагоне/скорпионе вполне реально. И документация, всё есть.Цитата:
Сообщение от NovaStorm
Почемуто всегда считал, что макрос и inline-процедуры работают быcтрее обычного вида подпрограмм, т.к. как раз не являются подпрограммами и не требуют вызова и сопутствующих манипуляций со стеком.Цитата:
Сообщение от fk0
Впрочем, утверждать не берусь.
Вообще-то, когда нужен 100% результат, концепции реализуются на языке блок-схем. И язык реализации в большинстве случаев принципиального значения не имеет - это лишь вопрос технологии и деталей реализации. Соответственно, в правильно спроектированных системах не возникает необходимости в спецсредствах типа ASM (ну кроме считанных time-critical мест или в случае программирования каких-то уж очень чудных микроконтроллеров).Цитата:
Сообщение от fk0
Про С в последних сообщениях этого треда говорилось с точки зрения переносимости кода с платформы на платформу. С этой точки зрения юзабельность АСМ стремится к нулю.
Ну да. А десят лет назад то же говорили про Basic. А еще раньше про Фортран. А еще через 20 лет будут говорить про что-то другое. Кстати, а есть ли Java-машины для Z80? Было бы интересно взглянуть...Цитата:
Сообщение от fk0
А что такого особенного в распределении памяти в C?Цитата:
Сообщение от fk0
Посыпаю голову пеплом от сожженых исходников и ухожу в монастырь. Женский :))))Цитата:
Сообщение от fk0
Я б сказал так- в асме реализуется все, ибо это праматерия. Другое дело что вместе с полной свободой действий мы получаем полуметровый геморрой с разработкой, рефакторингом, реверс инженирингом и отладкой...Цитата:
Сообщение от fk0
Это если имеется компилятор явы (или поддержка на аппаратном уровне)Цитата:
Сообщение от fk0
Ну дык, сравнил зетник и арм... Я, если честно, при написании под контроллер вообще не пользуюсь функциями выделения памяти, только статически все. Если надо- беру свой минималистический менеджер памяти и не мучаюсь.Цитата:
Сообщение от fk0
не, проект только под huge тянется...Цитата:
Сообщение от fk0
ясен хрен... неоднократно с этим сталкивался:Цитата:
Сообщение от fk0
xdata unsigned char* ext_dev _at_ 0xff00;
гораздо хуже чем
#define (*(xdata unsigned char*)0xff00) ext_dev
хотя по логике одно и то же...
приятное открытие- он берет 8051 и msp430. надо будет погонять как время будет....Цитата:
Сообщение от fk0
ЗЫ. А бесплатная версия имееццо? А то на сайте грят "только по талонам"...
Вах-вах! HI-TECH C Z80/Z180 = $950 - приятная цена! :)
последняя из CP/M-версий - бесплатноЦитата:
Сообщение от jdigreze
А версия компилятора Hi-tech, которую выкладывал https://zx-pk.ru/showthread.php?p=53431 не подходит?
Все попытки скачать файл с RapidShare лично для меня не увенчались успехом :)Цитата:
Сообщение от Aprisobal
Выдает:
You have requested the file Embedded_Systems_-_Avocet_WideUI_V3.28_ANSI-C_Compiler_for_Z80.rar (3749 KB).
This file has been downloaded 1 times already.
ERROR: Please enable JavaScript.
PS. Java у меня включена.
Скачал, проверяю пока первый.Цитата:
Сообщение от caro
Тестовый код (взято с codenet.ru):
Код:/* ---------------------------------------------------------- *
| |
| Серия тестов PC Tech Journal |
| Тест оптимизации кода Си |
| |
| Copyright (c) 1988 Ziff-Devis Publishing Company |
| |
| Эта программа-тест была разработана для проверки |
| методов оптимизации кода, применяемых компилятором |
| Си. Она не вырабатывает разумные результаты и не |
| представляет хороший стиль программирования. |
| |
* ---------------------------------------------------------- */
//#include
//#include
#define max_vector 2
#define constant5 5
typedef unsigned char uchar;
int i, j, k, l, m;
int i2, j2, k2;
int g3, h3, i3, k3, m3;
int i4, j4;
int i5, j5, k5;
double flt_1, flt_2, flt_3, flt_4, flt_5, flt_6;
int ivector[ 3 ];
uchar ivector2[ 3 ];
short ivector4[ 6 ];
int ivector5[ 100 ];
#ifndef NO_PROTOTYPES
void dead_code( int, char * );
void unnecessary_loop( void );
void loop_jamming( int );
void loop_unrolling( int );
int jump_compression( int, int, int, int, int );
#else
void dead_code();
void unnecessary_loop();
void loop_jamming();
void loop_unrolling();
int jump_compression();
#endif
int main( argc, argv ) /* optbench */
int argc;
char **argv;
{
/* ---------------------------- *
| Размножение констант и копий |
*------------------------------*/
j4 = 2;
if( i2 < j4 && i4 < j4 )
i2 = 2;
j4 = k5;
if( i2 < j4 && i4 < j4 )
i5 = 3;
/* ------------------------------------------ *
| Свертка констант, арифметические тождества |
| и излишние операции загрузки/сохранения |
* ------------------------------------------ */
i3 = 1 + 2;
flt_1 = 2.4 + 6.3;
i2 = 5;
j2 = i + 0;
k2 = i / 1;
i4 = i * 1;
i5 = i * 0;
#ifndef NO_ZERO_DIVIDE
/*
* Некоторые компиляторы распознают ошибку
* деления на нуль и не генерируют объектный код
*/
i2 = i / 0;
flt_2 = flt_1 / 0.0;
#else
printf( "This compiler handles divide-by-zero as \
an error\n");
#endif
flt_3 = 2.4 / 1.0;
flt_4 = 1.0 + 0.0000001;
flt_5 = flt_6 * 0.0;
flt_6 = flt_2 * flt_3;
/* -------------------- *
| Лишнее присваивание |
* -------------------- */
k3 = 1;
k3 = 1;
/* ------------------ *
| Снижение мощности |
* ------------------ */
k2 = 4 * j5;
for( i = 0; i <= 5; i++ )
ivector4[ i ] = i * 2;
/* ------------- *
| Простой цикл |
* ------------- */
j5 = 0;
k5 = 10000;
do {
k5 = k5 - 1;
j5 = j5 + 1;
i5 = (k5 * 3) / (j5 * constant5);
} while ( k5 > 0 );
/* -------------------------------------- *
| Управление переменной индукции цикла |
* -------------------------------------- */
for( i = 0; i < 100; i++ )
ivector5[ i * 2 + 3 ] = 5;
/* ----------------------- *
| Глубокие подвыражения |
* ----------------------- */
if( i < 10 )
j5 = i5 + i2;
else
k5 = i5 + i2;
/* ------------------------------------------------ *
| Проверка того, как компилятор генерирует адрес |
| переменной с константным индексом, размножает |
| копии и регистры |
* ------------------------------------------------ */
ivector[ 0 ] = 1; /* генерация константного адреса */
ivector[ i2 ] = 2; /* значение i2 должно быть скопировано*/
ivector[ i2 ] = 2; /* копирование регистров */
ivector[ 2 ] = 3; /* генарация константного адреса */
/* ----------------------------- *
| Удаление общих подвыражений |
* ----------------------------- */
if(( h3 + k3 ) < 0 || ( h3 + k3 ) > 5 )
printf("Common subexpression elimination\n");
else {
m3 = ( h3 + k3 ) / i3;
g3 = i3 + (h3 + k3);
}
/* -------------------------------------- *
| Вынесение инвариантного кода |
| (j * k) может быть вынесено из цикла |
* -------------------------------------- */
for( i4 = 0; i4 <= max_vector; i4++)
ivector2[ i4 ] = j * k;
/* ----------------------------- *
| Вызов функции с аргументами |
* ----------------------------- */
dead_code( 1, "This line should not be printed" );
/* ------------------------------ *
| Вызов функции без аргументов |
* ------------------------------ */
unnecessary_loop();
} /* Конец функции main */
/* ------------------------------------------------------ *
| Функция: dead_code |
| Проверка недостижимого кода и лишних |
| присваиваний. Не должен генерироваться код. |
* ------------------------------------------------------ */
void dead_code( a, b )
int a;
char *b;
{
int idead_store;
idead_store = a;
if( 0 )
printf( "%s\n", b );
} /* Конец dead_code */
/* ---------------------------------------------------- *
| Функция: unnecessary_loop |
| Цикл в следующей функции ненужен, так как |
| значение присваивания постоянно. В идеале |
| цикл должен быть удален. |
* ---------------------------------------------------- */
void unnecessary_loop()
{
int x;
x = 0;
for( i = 0; i < 5; i++ ) /* Цикл не должен
генерироваться*/
k5 = x + j5;
} /* Конец unnecessary_loop */
/* ---------------------------------------------------- *
| Функция: loop_jamming |
| Два цикла в этой функции имеют одинаковые |
| заголовки и могут быть слиты в один. |
* ---------------------------------------------------- */
void loop_jamming( x )
int x;
{
for( i = 0; i < 5; i++ )
k5 = x + j5 * i;
for( i = 0; i < 5; i++ )
i5 = x * k5 * i;
} /* Конец loop_jamming */
/* ------------------------------------------------------ *
| Функция: loop_unrolling |
| Цикл в этой функции должен быть заменен |
| тремя присваиваниями с использованием |
| константной индексации массива или машинно- |
| зависимыми командами для инициализации |
| блока памяти. |
* ------------------------------------------------------ */
void loop_unrolling( x )
int x;
{
for( i = 0; i < 6; i++ )
ivector4[ i ] = 0;
} /* Конец loop_unrolling */
/* ----------------------------------------------------- *
| Функция: jump_compression |
| Эта программа полезна для демонстрации |
| сжатия цепочки переходов. goto end_1 может |
| быть заменен на прямой переход на beg_1. |
* ----------------------------------------------------- */
int jump_compression( i, j, k, l, m )
int i, j, k, l, m;
{
beg_1:
if( i < j )
if( j < k )
if( k < l )
if( l < m )
l += m;
else
goto end_1;
else
k += l;
else {
j += k;
end_1:
goto beg_1;
}
else
i += j;
return( i + j + k + l + m );
} /* Конец jump_compression */
Ну что, прогнал HitechC по тесту, осталось весьма приятное впечатление. Некоторые вещи, конечно, хотелось бы улучшить, но все же.
Итак, что он, собственно накрутил. Испытания проводились с учетом максимального уровня оптимизации
0. Пролог. Весьма короткий:
дальше идет непосредственно кодКод:_main:
push ix
ld ix,0
add ix,sp
1. Тест на размножение констант и копий.
2.Свертка константКод:;TEST.C: 62: j4 = 2;
ld hl,02h
ld (_j4),hl
;TEST.C: 63: if( i2 < j4 && i4 < j4 )
;лишняя загрузка из j4 - следствие того, что для
;сравнения используется функция с фиксированными
;позициями для параметра, но все равно- мог бы
;использовать de для инициализации или сделать
;ld d,h:ld e,l
ld de,(_j4)
ld hl,(_i2)
call wrelop
;также использование jp не есть хорошо. сокращенные
;вычисления используются в соответствии со стандартом
jp p,l4
ld de,(_j4)
ld hl,(_i4)
call wrelop
jp p,l4
;TEST.C: 64: i2 = 2;
ld hl,02h
ld (_i2),hl
l4:
;TEST.C: 66: j4 = k5;
ld hl,(_k5)
ld (_j4),hl
;TEST.C: 67: if( i2 < j4 && i4 < j4 )
;картина полностью аналогичная
ld de,(_j4)
ld hl,(_i2)
call wrelop
jp p,l5
ld de,(_j4)
ld hl,(_i4)
call wrelop
;TEST.C: 68: i5 = 3;
ld hl,03h
jp p,L2
ld (_i5),hl
l5:
Код:;TEST.C: 75: i3 = 1 + 2;
;успешно
ld hl,03h
L2:
ld (_i3),hl
;TEST.C: 76: flt_1 = 2.4 + 6.3;
;аналогично
ld de,(e1)
ld hl,(e1+02h)
ld (_flt_1),de
ld (_flt_1+02h),hl
;TEST.C: 77: i2 = 5;
ld hl,05h
ld (_i2),hl
;TEST.C: 78: j2 = i + 0;
;ничего прибавлять не стал, молодец :)
ld hl,(_i)
ld (_j2),hl
;TEST.C: 79: k2 = i / 1;
;равно как не стал и делить, плюс не стал лишний раз
;делать загрузку!
ld (_k2),hl
;TEST.C: 80: i4 = i * 1;
;аналогично
ld (_i4),hl
;TEST.C: 81: i5 = i * 0;
;ну и умножение на ноль тоже распознал
ld hl,0
ld (_i5),hl
2.1.Ошибка деления на ноль была успешно распознана и компилятор ничего не собирал до тех пор, пока не определил NO_ZERO_DIVIDEКод:;TEST.C: 94: flt_3 = 2.4 / 1.0;
;свернул деление
ld de,(e2)
ld hl,(e2+02h)
ld (_flt_3),de
ld (_flt_3+02h),hl
;TEST.C: 95: flt_4 = 1.0 + 0.0000001;
;свернул сложение
ld de,(e3)
ld hl,(e3+02h)
ld (_flt_4),de
ld (_flt_4+02h),hl
;TEST.C: 96: flt_5 = flt_6 * 0.0;
;а вот здесь прокосячил- наверное нет проверки на 0
;для констант с плавающей запятой
ld hl,(e4+02h)
push hl
ld hl,(e4)
push hl
ld de,(_flt_6)
ld hl,(_flt_6+02h)
call flmul
ld (_flt_5),de
ld (_flt_5+02h),hl
;TEST.C: 97: flt_6 = flt_2 * flt_3;
;ничего интересного
ld hl,(_flt_3+02h)
push hl
ld hl,(_flt_3)
push hl
ld de,(_flt_2)
ld hl,(_flt_2+02h)
call flmul
ld (_flt_6),de
ld (_flt_6+02h),hl
3.Лишнее присваивание
4.Снижение мощностиКод:;TEST.C: 103: k3 = 1;
ld hl,01h
ld (_k3),hl
;TEST.C: 104: k3 = 1;
;сэкономил загрузку, но выполнил лишнее присваивание
;в силу того, что компилятор для встроенных систем на
;базе микроконтроллеров, повторное присваивание (а
;также чтение) имеют свой смысл. Так что это не ошибка
ld (_k3),hl
5.Простой циклКод:;TEST.C: 110: k2 = 4 * j5;
;вполне логично свернул умножение на 4
ld hl,(_j5)
add hl,hl
add hl,hl
ld (_k2),hl
;TEST.C: 111: for( i = 0; i <= 5; i++ )
;но просто отвратительно сделал цикл с изначально
;известными условиями- по шаблону
ld hl,0
ld (_i),hl
ld de,06h
ld hl,(_i)
call wrelop
jp p,l7
l6:
;TEST.C: 112: ivector4[ i ] = i * 2;
;чуда не произошло- снижения мощности нет
;в принципе, для такого шаблона без регистровых
;переменных это и логично...
ld de,_ivector4
ld hl,(_i)
add hl,hl
add hl,de
ex de,hl
ld hl,(_i)
add hl,hl
ld a,l
ld (de),a
inc de
ld a,h
ld (de),a
ld hl,(_i)
inc hl
ld (_i),hl
ld de,06h
call wrelop
jp m,l6
l7:
6.Управление переменной индукции циклаКод:;TEST.C: 118: j5 = 0;
ld hl,0
ld (_j5),hl
;TEST.C: 119: k5 = 10000;
ld hl,02710h
ld (_k5),hl
l11:
;TEST.C: 120: do {
;TEST.C: 121: k5 = k5 - 1;
;недостатки отсутствия регистровых переменных во
;всей красе...
ld hl,(_k5)
dec hl
ld (_k5),hl
;TEST.C: 122: j5 = j5 + 1;
ld hl,(_j5)
inc hl
ld (_j5),hl
;TEST.C: 123: i5 = (k5 * 3) / (j5 * 5);
;хотя тут их отсутствие вполне закономерно- иначе
;пришлось бы мучаться со стеком
;достойная свертка констант умножения
ld b,h
ld c,l
add hl,hl
add hl,hl
add hl,bc
ex de,hl
ld hl,(_k5)
ld b,h
ld c,l
add hl,hl
add hl,bc
call adiv
ld (_i5),hl
;TEST.C: 124: } while ( k5 > 0 );
;глупое сравнение на >=1...
ld de,01h
ld hl,(_k5)
call wrelop
jp p,l11
7.Глубокие подвыраженияКод:;TEST.C: 129: for( i = 0; i < 100; i++ )
;та же лажа с циклом
ld hl,0
ld (_i),hl
ld de,064h
call wrelop
jp p,l13
l12:
;TEST.C: 130: ivector5[ i * 2 + 3 ] = 5;
;переменной индукции нет (не регистровая)
;но смещение индекса преобразовано в смещение базы
;массива
ld hl,(_i)
add hl,hl
add hl,hl
ld de,_ivector5+06h
add hl,de
ld (hl),05h
inc hl
ld (hl),0
ld hl,(_i)
inc hl
ld (_i),hl
ld de,064h
call wrelop
jp m,l12
l13:
8.Генерация адреса переменной с константным индексом, размножение копий и регистровКод:;TEST.C: 136: if( i < 10 )
ld de,0Ah
ld hl,(_i)
call wrelop
;TEST.C: 137: j5 = i5 + i2;
;весьма хитро- загрузку параметров вынесли перед
;переходом, а сложение после, чтобы не портить флаг
ld hl,(_i5)
ld de,(_i2)
jp p,L1
add hl,de
ld (_j5),hl
;TEST.C: 138: else
jp l16
;TEST.C: 139: k5 = i5 + i2;
L1:
add hl,de
ld (_k5),hl
l16:
9.Удаление общих подвыраженийКод:;TEST.C: 147: ivector[ 0 ] = 1;
;логично
ld hl,01h
ld (_ivector),hl
;TEST.C: 148: ivector[ i2 ] = 2;
ld hl,(_i2)
add hl,hl
ld de,_ivector
add hl,de
ld (hl),02h
inc hl
ld (hl),0
;TEST.C: 149: ivector[ i2 ] = 2;
;повтор вычислений, но запоминание смещения-
;недостаток модели распределения регистров
ld hl,(_i2)
add hl,hl
add hl,de
ld (hl),02h
inc hl
ld (hl),0
;TEST.C: 150: ivector[ 2 ] = 3;
;тоже логично :)
ld hl,03h
ld (_ivector+04h),hl
10.Вынесение инвариантного кодаКод:;TEST.C: 157: if(( h3 + k3 ) < 0 || ( h3 + k3 ) > 5 )
ld hl,(_h3)
ld de,(_k3)
add hl,de
;наконец-то вспомнили о битовых операциях...
bit 07h,h
jp nz,u90
;а тут косяк- нахрена подобные вычисления?
;переменные ведь не volatile...
ld de,06h
ld hl,(_h3)
ld bc,(_k3)
add hl,bc
call wrelop
jp m,u91
u90:
;TEST.C: 158: printf("Common subexpression elimination\n");
ld hl,u29
push hl
call _printf
pop bc
;TEST.C: 159: else {
jp l18
u91:
;TEST.C: 160: m3 = ( h3 + k3 ) / i3;
;опять вычисления с нуля
ld de,(_i3)
ld hl,(_h3)
ld bc,(_k3)
add hl,bc
call adiv
ld (_m3),hl
;TEST.C: 161: g3 = i3 + (h3 + k3);
;фиаско! никаких промежуточных сохранений результатов
ld hl,(_i3)
ld de,(_h3)
add hl,de
ld de,(_k3)
add hl,de
ld (_g3),hl
l18:
;TEST.C: 162: }
Код:;TEST.C: 168: for( i4 = 0; i4 <= 2; i4++)
;все тот же бешеный цикл...
ld hl,0
ld (_i4),hl
ld de,03h
ld hl,(_i4)
call wrelop
jp p,l20
l19:
;TEST.C: 169: ivector2[ i4 ] = j * k;
;полная Ж... вычисление j * k в каждой итерации цикла
ld a,(_k)
ld e,a
ld d,0
ld a,(_j)
ld l,a
ld h,d
call lmul
ld a,l
ld hl,(_i4)
ld de,_ivector2
add hl,de
ld (hl),a
ld hl,(_i4)
inc hl
ld (_i4),hl
ld de,03h
call wrelop
jp m,l19
l20:
11.Вызов функции с аргументами. Определение недостижимого кода
Код:;TEST.C: 175: dead_code( 1, "This line should not be printed" );
;вызов типа fastcall- все что можно, через регистры
ld bc,u39
ld de,01h
call _dead_code
12.Вызов функции без аргументов. Исключение цикловКод:;TEST.C: 192: void dead_code( a, b )
;TEST.C: 193: int a;
;TEST.C: 194: char *b;
;TEST.C: 195: {
_dead_code:
;TEST.C: 196: int idead_store;
;TEST.C: 198: idead_store = a;
;о! первое использование регистровых
;переменных
; _idead_store allocated to hl
ld l,e
ld h,d
;TEST.C: 199: if( 0 )
;пустышка благополучно свернута. честь и хвала!
ret
;TEST.C: 201: }
Код:;TEST.C: 181: unnecessary_loop();
;простенько и со вкусом :)
call _unnecessary_loop
13. Слияние заголовков цикловКод:;TEST.C: 211: void unnecessary_loop()
;TEST.C: 212: {
_unnecessary_loop:
;TEST.C: 213: int x;
;TEST.C: 215: x = 0;
;еще одна регистровая переменная
; _x allocated to bc
ld bc,0
;TEST.C: 216: for( i = 0; i < 5; i++ )
;более глубокий анализ убрал бы этот цикл нафиг...
;но см. примечание про повторную загрузку
ld l,c
ld h,b
ld (_i),hl
ld de,05h
call wrelop
ret p
l25:
;TEST.C: 218: k5 = x + j5;
;не свернул х=0... никому не нужное сложение
ld hl,(_j5)
add hl,bc
ld (_k5),hl
ld hl,(_i)
inc hl
ld (_i),hl
ld de,05h
call wrelop
jp m,l25
;TEST.C: 219: }
ret
14.Разворачивание цикловКод:;TEST.C: 228: void loop_jamming( x )
;TEST.C: 229: int x;
;TEST.C: 230: {
_loop_jamming:
;пролог функции- выделение стека под локальные
;переменные и параметр
push ix
ld ix,0
add ix,sp
push bc
;_x stored from de
ld (ix+-2),e
ld (ix+-1),d
;TEST.C: 231: for( i = 0; i < 5; i++ )
ld hl,0
ld (_i),hl
ld de,05h
ld hl,(_i)
call wrelop
jp p,l30
l29:
;TEST.C: 232: k5 = x + j5 * i;
ld de,(_i)
ld hl,(_j5)
call amul
ld e,(ix+-2)
ld d,(ix+-1)
add hl,de
ld (_k5),hl
ld hl,(_i)
inc hl
ld (_i),hl
ld de,05h
call wrelop
jp m,l29
l30:
;TEST.C: 233: for( i = 0; i < 5; i++ )
;чуда нет- слияния заголовков циклов тоже
ld hl,0
ld (_i),hl
ld de,05h
ld hl,(_i)
call wrelop
jp p,l28
l32:
;TEST.C: 234: i5 = x * k5 * i;
ld de,(_k5)
ld l,(ix+-2)
ld h,(ix+-1)
call amul
ld de,(_i)
call amul
ld (_i5),hl
ld hl,(_i)
inc hl
ld (_i),hl
ld de,05h
call wrelop
jp m,l32
;TEST.C: 235: }
l28:
15.Сжатие цепочки переходовКод:;TEST.C: 246: void loop_unrolling( x )
;TEST.C: 247: int x;
;TEST.C: 248: {
_loop_unrolling:
;TEST.C: 249: for( i = 0; i < 6; i++ )
;тоже без чуда- возможно, число итераций цикла
;слишком велико для разворота, но опций не нашел
ld hl,0
ld (_i),hl
ld de,06h
ld hl,(_i)
call wrelop
ret p
l36:
;TEST.C: 250: ivector4[ i ] = 0;
ld hl,(_i)
add hl,hl
ld de,_ivector4
add hl,de
ld (hl),0
inc hl
ld (hl),0
ld hl,(_i)
inc hl
ld (_i),hl
ld de,06h
call wrelop
jp m,l36
;TEST.C: 251: }
ret
Фух! Вроде все сделал.Код:;TEST.C: 260: int jump_compression( i, j, k, l, m )
;TEST.C: 261: int i, j, k, l, m;
;TEST.C: 262: {
_jump_compression:
;монструозный пролог- много переменных на входе
;все равно не оптимально...
push ix
ld ix,0
add ix,sp
push bc
push bc
;_j stored from bc
ld (ix+-4),c
ld (ix+-3),b
;_i stored from de
ld (ix+-2),e
ld (ix+-1),d
l40:
; _l loaded to bc
ld c,(ix+6)
ld b,(ix+7)
;TEST.C: 263: beg_1:
;TEST.C: 264: if( i < j )
ld e,(ix+-4)
ld d,(ix+-3)
ld l,(ix+-2)
ld h,(ix+-1)
call wrelop
jp p,l41
;TEST.C: 265: if( j < k )
ld e,(ix+4)
ld d,(ix+5)
ld l,(ix+-4)
ld h,(ix+-3)
call wrelop
jp p,l42
;TEST.C: 266: if( k < l )
ld e,c
ld d,b
ld l,(ix+4)
ld h,(ix+5)
call wrelop
jp p,l43
;TEST.C: 267: if( l < m )
ld e,(ix+8)
ld d,(ix+9)
ld l,c
ld h,b
call wrelop
;сжатия нет- помешало невесть откуда взявшееся
;сохранение l (bc), хотя к данному моменту оно не менялось
jp p,l46
;TEST.C: 268: l += m;
ld l,(ix+8)
ld h,(ix+9)
add hl,bc
push hl
pop bc
;TEST.C: 269: else
jp l49
;TEST.C: 271: else
l43:
;TEST.C: 272: k += l;
ld l,(ix+4)
ld h,(ix+5)
add hl,bc
ld (ix+4),l
ld (ix+5),h
;TEST.C: 273: else {
jp l49
l42:
;TEST.C: 274: j += k;
ld e,(ix+4)
ld d,(ix+5)
ld l,(ix+-4)
ld h,(ix+-3)
add hl,de
ld (ix+-4),l
ld (ix+-3),h
l46:
;TEST.C: 275: end_1:
;_l stored from bc
ld (ix+6),c
ld (ix+7),b
;TEST.C: 276: goto beg_1;
jp l40
;TEST.C: 277: }
;TEST.C: 278: else
l41:
;TEST.C: 279: i += j;
ld e,(ix+-4)
ld d,(ix+-3)
ld l,(ix+-2)
ld h,(ix+-1)
add hl,de
ld (ix+-2),l
ld (ix+-1),h
l49:
;TEST.C: 280: return( i + j + k + l + m );
ld e,(ix+8)
ld d,(ix+9)
ld l,(ix+-4)
ld h,(ix+-3)
add hl,de
ld e,(ix+4)
ld d,(ix+5)
add hl,de
ld e,c
ld d,b
add hl,de
ld e,(ix+-2)
ld d,(ix+-1)
add hl,de
;TEST.C: 281: }
;эпилог- очистка стека, не рационально...
ld sp,ix
pop ix
pop bc
pop af
pop af
pop af
push bc
ret
Итоговая оценка (субъективно)- 8/10. Годится для быстрой проверки идей, для создания готовых программ следует подходить с осторожностью.
Камрады, протестите плз другие компилеры и подобным образом откомментируйте их код :)
Снял слепки с Avoset, sdcc, z88dk. В ближайшее время попробую проанализировать и их код. У кого еще какие компилеры завалялись?
Что заметно дешевле KEIL. Так что как сказать...Цитата:
Сообщение от jdigreze
Hitech могу дать.Цитата:
Сообщение от caro
Итак, препарируемый- Avocet. Этот компилятор не понял старого синтаксиса теста (см. заголовки функций), поэтому пришлось слегка его подработать напильником.
Вот что он нагенерил (лишние директивы вырезаны).
0.Пролог
1.Размножение констант и копийКод:;не суть важно, все равно один раз делается...
push ix
push de
push hl
ld ix,-?TSC0
add ix,sp
push af
2.Свертка константКод:ld hl,2
ld (_j4),hl
;лишняя операция
ld de,(_j4)
ld hl,(_i2)
and a
;использование нативных операций- плюс в копилку
sbc hl,de
;хитровымудренная система переходов- жирный минус
jp p,$+8
jp pe,?BOL0
jr +3
jp po,?BOL0
?ACC0: .equal $
ld de,(_j4)
ld hl,(_i4)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL0
jr +3
jp po,?BOL0
?PLC0: .equal $
ld hl,2
ld (_i2),hl
?BOL0: .equal $
;короче, никаких размножений констант не применяется
;и этот кусок- практически полная копия предыдущего
ld hl,(_k5)
ld (_j4),hl
ld e,l
ld d,h
ld hl,(_i2)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL1
jr +3
jp po,?BOL1
?ACC1: .equal $
ld de,(_j4)
ld hl,(_i4)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL1
jr +3
jp po,?BOL1
?PLC1: .equal $
ld hl,3
ld (_i5),hl
?BOL1: .equal $
3.Лишнее присваиваниеКод:;благополучно свернул целочисленные константы
ld hl,3
ld (_i3),hl
.const_data
?CGL0: .double 8.69999999999999930000
.program
;также свернул дробную
ld de,?CGL0
ld hl,_flt_1
call __store_double
;просто константа
ld hl,5
ld (_i2),hl
;а это убожество...
ld de,0
ld hl,(_i)
add hl,de
ld (_j2),hl
;здесь не сообразил что на 1 делить не надо...
ld de,1
ld hl,(_i)
call __div_int
ld (_k2),hl
;да и умножать тоже...
ld de,1
ld hl,(_i)
call __mult_int
ld (_i4),hl
;а это вообще хохма
ld de,0
ld hl,(_i)
call __mult_int
ld (_i5),hl
;и на 0 мы делить тоже умеем! :)
;как целочисленно...
ld de,0
ld hl,(_i)
call __div_int
ld (_i2),hl
.const_data
?CGL1: .double 0.0
.program
;...так и с плавающей запятой
ld de,?CGL1
ld hl,_flt_1
call __div_double
ex de,hl
ld hl,_flt_2
call __store_double
.const_data
?CGL2: .double 2.39999999999999990000
.program
;вот деление константы на константу он свернул
ld de,?CGL2
ld hl,_flt_3
call __store_double
.const_data
?CGL3: .double 1.00000010000000010000
.program
;сложение тоже
ld de,?CGL3
ld hl,_flt_4
call __store_double
.const_data
?CGL4: .double 0.0
.program
;но вот умножение на 0 не догадался облагородить
ld de,?CGL4
ld hl,_flt_6
call __mult_double
ex de,hl
ld hl,_flt_5
call __store_double
;ничего интересного
ld de,_flt_3
ld hl,_flt_2
call __mult_double
ex de,hl
ld hl,_flt_6
call __store_double
4.Снижение мощностиКод:;за такое надо руки отрывать...
ld hl,1
ld (_k3),hl
ld hl,1
ld (_k3),hl
5.Простой циклКод:;никакой фантазии...
ld de,(_j5)
ld hl,4
call __mult_int
ld (_k2),hl
ld hl,0
ld (_i),hl
?FLC2: .equal $
ld de,5
ld hl,(_i)
and a
sbc hl,de
;опять сложная система переходов
jr z,+11
jp p,$+8
jp pe,?BOL2
jr +3
jp po,?BOL2
jp ?FLB2
?FLA2: .equal $
;регистровые переменные не поддерживаются,
;по крайней мере здесь
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC2
?FLB2: .equal $
;это просто мрак...
ld de,2
ld hl,(_i)
call __mult_int
ld (ix+0),l
ld (ix+0+1),h
ld hl,(_i)
add hl,hl
ld bc,_ivector4
add hl,bc
ld e,l
ld d,h
;люди, не смотрите на это :)
ld l,(ix+0)
ld h,(ix+0+1)
push de
pop iy
ld (iy+0),l
ld (iy+1),h
jp ?FLA2
?BOL2: .equal $
6.Управление переменной индукции циклаКод:ld hl,0
ld (_j5),hl
ld hl,10000
ld (_k5),hl
?TOL3: .equal $
;просто ни ума, ни фантазии...
ld de,1
ld hl,(_k5)
and a
sbc hl,de
ld (_k5),hl
ld de,1
ld hl,(_j5)
add hl,de
ld (_j5),hl
ld de,3
ld hl,(_k5)
call __mult_int
ld (ix+0),l
ld (ix+0+1),h
ld de,5
ld hl,(_j5)
call __mult_int
ld e,l
ld d,h
ld l,(ix+0)
ld h,(ix+0+1)
call __div_int
ld (_i5),hl
?DCL3: .equal $
;просто верх кодерского разума- сравнение на 0...
ld de,0
ld hl,(_k5)
and a
sbc hl,de
jp z,?BOL3
jp p,$+8
jp po,?BOL3
jr +3
jp pe,?BOL3
?PLC2: .equal $
jp ?TOL3
?BOL3: .equal $
7. Глубокие подвыраженияКод:;достаточно приличный заголовок цикла
ld hl,0
ld (_i),hl
?FLC4: .equal $
ld de,100
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL4
jr +3
jp po,?BOL4
jp ?FLB4
?FLA4: .equal $
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC4
?FLB4: .equal $
;а здесь начинаются страшные вычисления без каких
;бы то ни было следов оптимизации
ld de,2
ld hl,(_i)
call __mult_int
ld e,l
ld d,h
ld hl,3
add hl,de
add hl,hl
ld bc,_ivector5
add hl,bc
ld e,l
ld d,h
ld hl,5
push de
pop iy
ld (iy+0),l
ld (iy+1),h
jp ?FLA4
?BOL4: .equal $
8.Генерация адреса переменной с константным индексом, размножение копий и регистровКод:;без слов. все и так видно...
ld de,10
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL5
jr +3
jp po,?BOL5
?PLC3: .equal $
ld de,(_i2)
ld hl,(_i5)
add hl,de
ld (_j5),hl
jp ?EOI5
?BOL5: .equal $
ld de,(_i2)
ld hl,(_i5)
add hl,de
ld (_k5),hl
?EOI5: .equal $
9.Удаление общих подвыраженийКод:ld hl,_ivector+0
ld e,l
ld d,h
ld hl,1
;ребята наверное не знают про команду ld (hl),..
push de
pop iy
ld (iy+0),l
ld (iy+1),h
ld hl,(_i2)
add hl,hl
ld bc,_ivector
add hl,bc
ld e,l
ld d,h
ld hl,2
push de
pop iy
ld (iy+0),l
ld (iy+1),h
ld hl,(_i2)
add hl,hl
ld bc,_ivector
add hl,bc
ld e,l
ld d,h
ld hl,2
push de
pop iy
ld (iy+0),l
ld (iy+1),h
ld hl,_ivector+4
ld e,l
ld d,h
ld hl,3
push de
pop iy
ld (iy+0),l
ld (iy+1),h
10.Вынесение инвариантного кодаКод:ld de,(_k3)
ld hl,(_h3)
add hl,de
ld de,0
and a
sbc hl,de
jp p,$+8
jp po,?PLC4
jr +3
jp pe,?PLC4
?OCC2: .equal $
ld de,(_k3)
ld hl,(_h3)
add hl,de
ld de,5
and a
sbc hl,de
jp z,?BOL6
jp p,$+8
jp po,?BOL6
jr +3
jp pe,?BOL6
?PLC4: .equal $
.const_data
?PLC5: .byte "Common subexpression elimination",0ah,0
.program
ld hl,?PLC5
call _printf
jp ?EOI6
?BOL6: .equal $
;иными словами, никаких выделений не производится
ld de,(_k3)
ld hl,(_h3)
add hl,de
ld de,(_i3)
call __div_int
ld (_m3),hl
ld de,(_k3)
ld hl,(_h3)
add hl,de
ld e,l
ld d,h
ld hl,(_i3)
add hl,de
ld (_g3),hl
?EOI6: .equal $
Код:;приемлимое начало цикла
ld hl,0
ld (_i4),hl
?FLC7: .equal $
ld de,2
ld hl,(_i4)
and a
sbc hl,de
jr z,+11
;но его структура в коде просто ужасна!
jp p,$+8
jp pe,?BOL7
jr +3
jp po,?BOL7
jp ?FLB7
?FLA7: .equal $
ld hl,(_i4)
inc hl
ld (_i4),hl
jp ?FLC7
?FLB7: .equal $
;никакого вынесения нет- вычисления на каждой итерации
ld de,(_k)
ld hl,(_j)
call __mult_int
ld (ix+0),l
ld (ix+0+1),h
ld hl,(_i4)
ld bc,_ivector2
add hl,bc
ld e,l
ld d,h
ld l,(ix+0)
ld h,(ix+0+1)
ld a,l
ld (de),a
jp ?FLA7
11.Вызов функции с аргументами. Определение недостижимого кода
Код:.const_data
?PLC6: .byte "This line should not be printed",0
.program
;хоть тут никаких изысков, параметры в регистрах
ld de,?PLC6
ld hl,1
call _dead_code
12.Вызов функции без аргументов. Исключение цикловКод:_dead_code: .equal $
;страшный пролог!
push ix
push de
push hl
ld ix,-?TSC1
add ix,sp
push af
ld l,(ix+0+?TSC1)
ld h,(ix+0+?TSC1+1)
ld (ix-2),l
ld (ix-2+1),h
;страх и ужас!
ld a,0
cp a,0
jp z,?BOL8
?PLC7: .equal $
.const_data
?PLC8: .byte "%s",0ah,0
.program
ld e,(ix+2+?TSC1)
ld d,(ix+2+?TSC1+1)
ld hl,?PLC8
call _printf
?BOL8: .equal $
?BOF1: .equal $
;и не менее страшный эпилог
pop bc
pop bc
pop bc
pop ix
ret
Код:;как обычно, ничего никуда не передаем
call _unnecessary_loop
13. Слияние заголовков цикловКод:_unnecessary_loop: .equal $
push ix
ld ix,-?TSC2
add ix,sp
push af
;все тот же кошмарный пролог...
ld hl,0
ld (ix-2),l
ld (ix-2+1),h
ld hl,0
ld (_i),hl
?FLC9: .equal $
ld de,5
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL9
jr +3
jp po,?BOL9
jp ?FLB9
?FLA9: .equal $
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC9
?FLB9: .equal $
;постоянные вычисления, тут даже пресловутое
;примечание опускает руки :)
ld de,(_j5)
ld l,(ix-2)
ld h,(ix-2+1)
add hl,de
ld (_k5),hl
jp ?FLA9
?BOL9: .equal $
?BOF2: .equal $
pop bc
pop ix
ret
14.Разворачивание цикловКод:_loop_jamming: .equal $
push ix
push hl
ld ix,-?TSC3
add ix,sp
ld hl,0
ld (_i),hl
?FLC10: .equal $
ld de,5
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL10
jr +3
jp po,?BOL10
jp ?FLB10
?FLA10: .equal $
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC10
?FLB10: .equal $
ld de,(_i)
ld hl,(_j5)
call __mult_int
ld e,l
ld d,h
ld l,(ix+0+?TSC3)
ld h,(ix+0+?TSC3+1)
add hl,de
ld (_k5),hl
jp ?FLA10
?BOL10: .equal $
;никакого слияния плюс страшные вычисления
ld hl,0
ld (_i),hl
?FLC11: .equal $
ld de,5
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL11
jr +3
jp po,?BOL11
jp ?FLB11
?FLA11: .equal $
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC11
?FLB11: .equal $
ld de,(_k5)
ld l,(ix+0+?TSC3)
ld h,(ix+0+?TSC3+1)
call __mult_int
ld e,l
ld d,h
ld hl,(_i)
call __mult_int
ld (_i5),hl
jp ?FLA11
?BOL11: .equal $
?BOF3: .equal $
pop bc
pop ix
ret
15.Сжатие цепочки переходовКод:_loop_unrolling: .equal $
push ix
push hl
ld ix,-?TSC4
add ix,sp
ld hl,0
ld (_i),hl
?FLC12: .equal $
;разворота нет
ld de,6
ld hl,(_i)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL12
jr +3
jp po,?BOL12
jp ?FLB12
?FLA12: .equal $
ld hl,(_i)
inc hl
ld (_i),hl
jp ?FLC12
?FLB12: .equal $
;да еще и вычисления страшные
ld hl,(_i)
;хоть не догадался умножать на 2 процедурой...
add hl,hl
ld bc,_ivector4
add hl,bc
ld e,l
ld d,h
ld hl,0
push de
pop iy
ld (iy+0),l
ld (iy+1),h
jp ?FLA12
?BOL12: .equal $
?BOF4: .equal $
pop bc
pop ix
ret
Итоговая оценка- 4/10. Поставил бы 3/10, да понравился достаточно легкий заголовок цикла.Код:_jump_compression: .equal $
push ix
push de
push hl
ld ix,-?TSC5
add ix,sp
.c_start 0ba1h,260
?beg_1: .equal $
ld e,(ix+2+?TSC5)
ld d,(ix+2+?TSC5+1)
ld l,(ix+0+?TSC5)
ld h,(ix+0+?TSC5+1)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL13
jr +3
jp po,?BOL13
?PLC9: .equal $
ld e,(ix+8+?TSC5)
ld d,(ix+8+?TSC5+1)
ld l,(ix+2+?TSC5)
ld h,(ix+2+?TSC5+1)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL14
jr +3
jp po,?BOL14
?PLC10: .equal $
ld e,(ix+10+?TSC5)
ld d,(ix+10+?TSC5+1)
ld l,(ix+8+?TSC5)
ld h,(ix+8+?TSC5+1)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL15
jr +3
jp po,?BOL15
?PLC11: .equal $
ld e,(ix+12+?TSC5)
ld d,(ix+12+?TSC5+1)
ld l,(ix+10+?TSC5)
ld h,(ix+10+?TSC5+1)
and a
sbc hl,de
jp p,$+8
jp pe,?BOL16
jr +3
jp po,?BOL16
?PLC12: .equal $
ld e,(ix+12+?TSC5)
ld d,(ix+12+?TSC5+1)
ld l,(ix+10+?TSC5)
ld h,(ix+10+?TSC5+1)
add hl,de
ld (ix+10+?TSC5),l
ld (ix+10+?TSC5+1),h
jp ?EOI16
?BOL16: .equal $
;нет оптимизации
jp ?end_1
?EOI16: .equal $
jp ?EOI15
?BOL15: .equal $
ld e,(ix+10+?TSC5)
ld d,(ix+10+?TSC5+1)
ld l,(ix+8+?TSC5)
ld h,(ix+8+?TSC5+1)
add hl,de
ld (ix+8+?TSC5),l
ld (ix+8+?TSC5+1),h
?EOI15: .equal $
jp ?EOI14
?BOL14: .equal $
ld e,(ix+8+?TSC5)
ld d,(ix+8+?TSC5+1)
ld l,(ix+2+?TSC5)
ld h,(ix+2+?TSC5+1)
add hl,de
ld (ix+2+?TSC5),l
ld (ix+2+?TSC5+1),h
?end_1: .equal $
jp ?beg_1
?EOI14: .equal $
jp ?EOI13
?BOL13: .equal $
ld e,(ix+2+?T
;а дальше текст почемуто потерян...
Плюс данный компилятор распространяетя со средой разработки и отладки под винду. Опций настройки я не нашел- они задавались в настройках проекта, но в хелпе я нашел не все указанные опции. Про оптимизацию там не было сказано ни слова. Итого- это просто транслятор с примитивной сверткой констант (и только!). Выходной ассемблерный файл без указания строк исходного файла, весьма неудобно по нему лазить.
Короче, использовать не рекомендуется :)
Следующий кандидат- Small Device C Compiler
Испытания проводились с оптимизацией по размеру и по скорости- результат одинаковый.
0. Пролог.
1. Тест на размножение констант и копий.Код:;классика жанра для переменных в стеке
_main:
push ix
ld ix,#0
add ix,sp
2.Свертка константКод:;жирный-жирный минус- использование тяжелой
;артиллерии в виде индексных регистров
;test.c:59: j4 = 2;
ld iy,#_j4
ld 0(iy),#0x02
ld 1(iy),#0x00
;test.c:60: if( i2 < j4 && i4 < j4 )
;вот оно- распространение констант в действии!!!
ld iy,#_i2
;вместо вычитания j4 используется вычитание константы
ld a,0(iy)
sub a,#0x02
ld a,1(iy)
sbc a,#0x00
jp p,00102$
;аналогично вторая половина
ld iy,#_i4
ld a,0(iy)
sub a,#0x02
ld a,1(iy)
sbc a,#0x00
jp p,00102$
;test.c:61: i2 = 2;
;опять IY...
ld iy,#_i2
ld 0(iy),#0x02
ld 1(iy),#0x00
00102$:
;test.c:63: j4 = k5;
;ну почему загрузка сделана нормально, но сохранение
;так лажово?
ld hl,(_k5)
ld iy,#_j4
ld 0(iy),l
ld 1(iy),h
;test.c:64: if( i2 < j4 && i4 < j4 )
;просто страшная перезагрузка!!! нет использования
;предыдущего операнда в hl!!!
ld iy,#_i2
ld a,0(iy)
ld iy,#_j4
sub a,0(iy)
ld iy,#_i2
ld a,1(iy)
ld iy,#_j4
sbc a,1(iy)
jp p,00105$
;то же самое...
ld iy,#_i4
ld a,0(iy)
ld iy,#_j4
sub a,0(iy)
ld iy,#_i4
ld a,1(iy)
ld iy,#_j4
sbc a,1(iy)
jp p,00105$
;test.c:65: i5 = 3;
ld iy,#_i5
ld 0(iy),#0x03
ld 1(iy),#0x00
00105$:
3.Лишнее присваиваниеКод:;test.c:72: i3 = 1 + 2;
;свернул
ld iy,#_i3
ld 0(iy),#0x03
ld 1(iy),#0x00
;test.c:73: flt_1 = 2.4 + 6.3;
;вот здесь использование индексных регистров можно
;оправдать, да и то с натяжкой...
ld iy,#_flt_1
ld 0(iy),#0x33
ld 1(iy),#0x33
ld 2(iy),#0x0B
ld 3(iy),#0x41
;test.c:74: i2 = 5;
ld iy,#_i2
ld 0(iy),#0x05
ld 1(iy),#0x00
;test.c:75: j2 = i + 0;
;0 не стал добавлять, просто копирование
ld hl,(_i)
ld iy,#_j2
ld 0(iy),l
ld 1(iy),h
;test.c:76: k2 = i / 1;
;и на 1 делить не стал
ld hl,(_i)
ld iy,#_k2
ld 0(iy),l
ld 1(iy),h
;test.c:77: i4 = i * 1;
;да и умножать тоже
ld hl,(_i)
ld iy,#_i4
ld 0(iy),l
ld 1(iy),h
;test.c:78: i5 = i * 0;
;сообразительный малый :)
ld iy,#_i5
ld 0(iy),#0x00
ld 1(iy),#0x00
;test.c:88: printf( "This compiler handles divide-by-zero as
;деление на 0 не словил на этапе комппиляции, пытается
;печатать
;передача параметров через стек- не очень хорошо...
ld hl,#__str_0
push hl
call _printf
;и очистка вызывающей функцией- классическая
;парадигма _cdecl
pop af
;test.c:91: flt_3 = 2.4 / 1.0;
;с плавающей запятой константы тоже сворачивает
ld iy,#_flt_3
ld 0(iy),#0x9A
ld 1(iy),#0x99
ld 2(iy),#0x19
ld 3(iy),#0x40
;test.c:92: flt_4 = 1.0 + 0.0000001;
ld iy,#_flt_4
ld 0(iy),#0x01
ld 1(iy),#0x00
ld 2(iy),#0x80
ld 3(iy),#0x3F
;test.c:93: flt_5 = flt_6 * 0.0;
;молодец :)
ld iy,#_flt_5
ld 0(iy),#0x00
ld 1(iy),#0x00
ld 2(iy),#0x00
ld 3(iy),#0x00
;test.c:94: flt_6 = flt_2 * flt_3;
;распространение константы flt_3 = 2.4
ld hl,#0x4019
push hl
ld hl,#0x999A
push hl
ld hl,(_flt_2 + 2)
push hl
ld hl,(_flt_2)
push hl
call ___fsmul
;заволновался по поводу hl...
ld b,h
ld c,l
;освобождение стека- 8 байт
pop af
pop af
pop af
pop af
ld iy,#_flt_6
ld 0(iy),c
ld 1(iy),b
ld 2(iy),e
ld 3(iy),d
4.Снижение мощностиКод:;test.c:101: k3 = 1;
;было благополучно удалено!
ld iy,#_k3
ld 0(iy),#0x01
ld 1(iy),#0x00
5.Простой циклКод:;test.c:107: k2 = 4 * j5;
;это просто "шедевр"! это надо видеть!!!
ld iy,#_j5
ld a,0(iy)
ld iy,#_k2
ld 0(iy),a
ld iy,#_j5
ld a,1(iy)
ld iy,#_k2
ld 1(iy),a
;впервые на манеже ТАКИЕ извращения при попытке
;заменить умножение сдвигами :)
ld a,#0x02+1
jp 00146$
00145$:
ld iy,#_k2
sla 0(iy)
rl 1(iy)
00146$:
dec a
jp nz,00145$
;test.c:108: for( i = 0; i <= 5; i++ )
;переменная цикла тоже в индексном... грустно...
ld iy,#_i
ld 0(iy),#0x00
ld 1(iy),#0x00
00117$:
ld a,#0x05
ld iy,#_i
sub a,0(iy)
ld a,#0x00
sbc a,1(iy)
jp m,00120$
;test.c:109: ivector4[ i ] = i * 2;
;мрачно...
ld c,0(iy)
ld b,1(iy)
sla c
rl b
ld hl,#_ivector4
add hl,bc
;так и не понял, для чего эти две команды...
ld e,l
ld d,h
ld (hl),c
inc hl
ld (hl),b
;test.c:108: for( i = 0; i <= 5; i++ )
;а вот это небольшой финт- полупересчетка
ld iy,#_i
inc 0(iy)
jp nz,00149$
inc 1(iy)
00149$:
jp 00117$
00120$:
6.Управление переменной индукции циклаКод:;test.c:115: j5 = 0;
ld iy,#_j5
ld 0(iy),#0x00
ld 1(iy),#0x00
;test.c:116: k5 = 10000;
ld iy,#_k5
ld 0(iy),#0x10
ld 1(iy),#0x27
;test.c:117: do {
00107$:
;test.c:118: k5 = k5 - 1;
;ну достаточно логичная конструкция- почему в остальных
;местах IY????
ld hl,(_k5)
dec hl
ld (_k5),hl
;test.c:119: j5 = j5 + 1;
;например, здесь????
ld iy,#_j5
inc 0(iy)
jp nz,00150$
inc 1(iy)
00150$:
;test.c:120: i5 = (k5 * 3) / (j5 * constant5);
;ну просто конфетка! :)
ld de,(_k5)
ld l,e
ld h,d
add hl,hl
add hl,de
ld c,l
ld b,h
;он даже константу развернул!!!
ld de,(_j5)
ld l,e
ld h,d
add hl,hl
add hl,hl
add hl,de
ld e,l
ld d,h
;параметры через стек
push de
push bc
call __divsint_rrx_s
;непонятного назначения конструкция...
ld b,h
ld c,l
pop af
pop af
ld iy,#_i5
ld 0(iy),c
ld 1(iy),b
;test.c:121: } while ( k5 > 0 );
;страшная конструкция...
ld a,#0x00
ld iy,#_k5
sub a,0(iy)
ld a,#0x00
sbc a,1(iy)
jp m,00107$
7.Глубокие подвыраженияКод:;test.c:126: for( i = 0; i < 100; i++ )
;стандартный заголовок цикла
ld iy,#_i
ld 0(iy),#0x00
ld 1(iy),#0x00
00121$:
ld iy,#_i
ld a,0(iy)
sub a,#0x64
ld a,1(iy)
sbc a,#0x00
jp p,00124$
;test.c:127: ivector5[ i * 2 + 3 ] = 5;
;выделение индукции нет, но интересно- сообразил, что
; переменная цикла помещается в один байт
ld c,0(iy)
ld a,c
add a,a
ld c,a
;эх, не догадался базу массива сместить :)
inc c
inc c
inc c
;не забываем про размер
ld a,c
add a,a
ld c,a
;вот это интересно- использование полуслов адреса
ld a,#<_ivector5
add a,c
ld c,a
ld a,#>_ivector5
adc a,#0x00
ld b,a
;бережет он HL непонятно с какими целями...
ld l,c
ld h,b
ld (hl),#0x05
inc hl
ld (hl),#0x00
;test.c:126: for( i = 0; i < 100; i++ )
;стандарный инкремент
inc 0(iy)
jp nz,00151$
inc 1(iy)
00151$:
jp 00121$
00124$:
8.Генерация адреса переменной с константным индексом, размножение копий и регистровКод:;test.c:133: if( i < 10 )
ld iy,#_i
ld a,0(iy)
sub a,#0x0A
ld a,1(iy)
sbc a,#0x00
jp p,00111$
;test.c:134: j5 = i5 + i2;
;страхолюдная конструкция...
ld hl,#_i2
push de
ld iy,#_j5
push iy
pop de
ld iy,#_i5
ld a,0(iy)
add a,(hl)
ld (de),a
ld a,1(iy)
inc hl
adc a,(hl)
inc de
ld (de),a
pop de
jp 00112$
00111$:
;test.c:136: k5 = i5 + i2;
;иными словами- никакого выделения общего подвыражения нет
ld hl,#_i2
push de
ld iy,#_k5
push iy
pop de
ld iy,#_i5
ld a,0(iy)
add a,(hl)
ld (de),a
ld a,1(iy)
inc hl
adc a,(hl)
inc de
ld (de),a
pop de
00112$:
9.Удаление общих подвыраженийКод:;test.c:144: ivector[ 0 ] = 1;
;ну почему в обычном варианте этого нет???
ld hl,#_ivector
ld (hl),#0x01
inc hl
ld (hl),#0x00
;test.c:145: ivector[ i2 ] = 2;
;страсти-то какие...
ld iy,#_i2
ld c,0(iy)
ld b,1(iy)
sla c
rl b
dec hl
add hl,bc
ld c,l
ld b,h
ld (hl),#0x02
inc hl
ld (hl),#0x00
;test.c:146: ivector[ i2 ] = 2;
;эти две команды лишние, но в целом используется
;результат предыдущих вычислений
ld l,c
ld h,b
ld (hl),#0x02
inc hl
ld (hl),#0x00
;test.c:147: ivector[ 2 ] = 3;
;почему так странно?
ld bc,#_ivector + 4
ld l,c
ld h,b
ld (hl),#0x03
inc hl
ld (hl),#0x00
10.Вынесение инвариантного кодаКод:;test.c:154: if(( h3 + k3 ) < 0 || ( h3 + k3 ) > 5 )
;советую присмотреться внимательно- компилятор
;запомнил, что k3 не менялся и равен 1- размножение
;константы плюс сокращенные вычисления плюс сразу
;два сравнения
;ЗЫ. закрывайте глаза на реализацию идеи :)
ld iy,#_h3
ld c,0(iy)
ld b,1(iy)
;вот это мы добавляем к3
inc bc
;проверка на <0 - проще простого
ld a,b
bit 7,a
jp nz,00113$
;а также на >5
ld a,#0x05
sub a,c
ld a,#0x00
sbc a,b
jp p,00114$
00113$:
;test.c:155: printf("Common subexpression elimination\n");
;параметры через стек
ld hl,#__str_1
push hl
call _printf
pop af
jp 00115$
00114$:
;test.c:157: m3 = ( h3 + k3 ) / i3;
;выделение общих подвыражений действует!- выражение
;в скобках не вычисляется повторно, а хранится в bc
push bc
ld hl,(_i3)
push hl
push bc
call __divsint_rrx_s
ld d,h
ld e,l
pop af
pop af
pop bc
ld iy,#_m3
ld 0(iy),e
ld 1(iy),d
;test.c:158: g3 = i3 + (h3 + k3);
;аналогично и здесь
ld hl,#_g3
ld iy,#_i3
ld a,0(iy)
add a,c
ld (hl),a
ld a,1(iy)
adc a,b
inc hl
ld (hl),a
00115$:
Код:;test.c:166: for( i4 = 0; i4 <= max_vector; i4++)
ld iy,#_i4
ld 0(iy),#0x00
ld 1(iy),#0x00
00125$:
ld a,#0x02
ld iy,#_i4
sub a,0(iy)
ld a,#0x00
sbc a,1(iy)
jp m,00128$
;test.c:167: ivector2[ i4 ] = j * k;
;весьма оригинальное разыменование указателя
ld hl,#_i4
ld a,#<_ivector2
add a,(hl)
ld c,a
ld a,#>_ivector2
inc hl
adc a,(hl)
ld b,a
;но вынесения инвариантной части нет- каждый цикл
;выполняются вычисления
ld iy,#_j
ld e,0(iy)
ld iy,#_k
ld d,0(iy)
push bc
push de
inc sp
ld a,e
push af
inc sp
call __mulschar_rrx_s
ld e,l
pop af
pop bc
ld a,e
ld (bc),a
;test.c:166: for( i4 = 0; i4 <= max_vector; i4++)
ld iy,#_i4
inc 0(iy)
jp nz,00154$
inc 1(iy)
00154$:
jp 00125$
00128$:
11.Вызов функции с аргументами. Определение недостижимого кода
Код:;test.c:173: dead_code( 1, "This line should not be printed" );
;обычная передача параметров через стек и очистка
ld hl,#__str_2
push hl
ld hl,#0x0001
push hl
call _dead_code
pop af
pop af
12.Вызов функции без аргументов. Исключение цикловКод:_dead_code:
;лишнее выделение стекового места, но полная оптимизация
;внутренностей функции
push ix
ld ix,#0
add ix,sp
;test.c:196: printf( "%s\n", b );
00103$:
pop ix
ret
Код:;test.c:179: unnecessary_loop();
call _unnecessary_loop
00129$:
13. Слияние заголовков цикловКод:_unnecessary_loop:
;test.c:212: for( i = 0; i < 5; i++ )
ld iy,#_i
;обратите внимание- компилятор перевернул цикл!
ld 0(iy),#0x05
ld 1(iy),#0x00
00103$:
;test.c:214: k5 = x + j5;
;размножение константы х = 0
;но повторная загрузка имеется, простительно
ld hl,(_j5)
ld iy,#_k5
ld 0(iy),l
ld 1(iy),h
ld hl,(_i)
dec hl
ld (_i),hl
;test.c:212: for( i = 0; i < 5; i++ )
;оригинальная концовка!
ld iy,#_i
ld a,0(iy)
or a,1(iy)
jp nz,00103$
;а этот код потому что i является глобально переменной
ld 0(iy),#0x05
ld 1(iy),#0x00
00104$:
ret
14.Разворачивание цикловКод:_loop_jamming:
push ix
ld ix,#0
add ix,sp
;test.c:226: for( i = 0; i < 5; i++ )
ld iy,#_i
ld 0(iy),#0x00
ld 1(iy),#0x00
00101$:
ld iy,#_i
ld a,0(iy)
sub a,#0x05
ld a,1(iy)
sbc a,#0x00
jp p,00104$
;test.c:227: k5 = x + j5 * i;
ld hl,(_i)
push hl
ld hl,(_j5)
push hl
call __mulint_rrx_s
ld b,h
ld c,l
pop af
pop af
;переменная x хранится в стеке
ld hl,#_k5
ld a,4(ix)
add a,c
ld (hl),a
ld a,5(ix)
adc a,b
inc hl
ld (hl),a
;test.c:226: for( i = 0; i < 5; i++ )
ld iy,#_i
inc 0(iy)
jp nz,00115$
inc 1(iy)
00115$:
jp 00101$
00104$:
;test.c:228: for( i = 0; i < 5; i++ )
;слияния заголовков нет
ld iy,#_i
ld 0(iy),#0x00
ld 1(iy),#0x00
00105$:
ld iy,#_i
ld a,0(iy)
sub a,#0x05
ld a,1(iy)
sbc a,#0x00
jp p,00109$
;test.c:229: i5 = x * k5 * i;
ld hl,(_k5)
push hl
ld l,4(ix)
ld h,5(ix)
push hl
call __mulint_rrx_s
ld b,h
ld c,l
pop af
pop af
ld hl,(_i)
push hl
push bc
call __mulint_rrx_s
ld b,h
ld c,l
pop af
pop af
ld iy,#_i5
ld 0(iy),c
ld 1(iy),b
;test.c:228: for( i = 0; i < 5; i++ )
ld iy,#_i
inc 0(iy)
jp nz,00116$
inc 1(iy)
00116$:
jp 00105$
00109$:
pop ix
ret
15.Сжатие цепочки переходовКод:_loop_unrolling:
push ix
ld ix,#0
add ix,sp
;test.c:243: for( i = 0; i < 6; i++ )
;разворота нет
ld iy,#_i
ld 0(iy),#0x00
ld 1(iy),#0x00
00101$:
ld iy,#_i
ld a,0(iy)
sub a,#0x06
ld a,1(iy)
sbc a,#0x00
jp p,00105$
;test.c:244: ivector4[ i ] = 0;
;экзотическая загрузка :)
ld c,0(iy)
ld b,1(iy)
sla c
rl b
ld hl,#_ivector4
add hl,bc
ld c,l
ld b,h
ld (hl),#0x00
inc hl
ld (hl),#0x00
;test.c:243: for( i = 0; i < 6; i++ )
ld iy,#_i
inc 0(iy)
jp nz,00111$
inc 1(iy)
00111$:
jp 00101$
00105$:
pop ix
ret
Итоговая оценка- 7/10. Умное распространение констант, сжатие переходов, приемлимый заголовок цикла, различная оптимизация, но повсеместное использование индексного регистра для хранения глобальных переменных все портит. Надо будет проверить как он себя ведет на статических переменных и возможно ли убрать работу с индексными регистрами. Если получится, то данный компилятор можно рекомендовать к применению! :)Код:_jump_compression:
push ix
ld ix,#0
add ix,sp
;test.c:256: beg_1:
ld a,8(ix)
sub a,10(ix)
ld a,9(ix)
sbc a,11(ix)
;интересный финт- в С результат сравнения k и l
rlca
and a,#0x01
ld c,a
ld a,10(ix)
sub a,12(ix)
ld a,11(ix)
sbc a,13(ix)
;а в А результат сравнения l и m
rlca
and a,#0x01
;только он почему-то не используется, а теряется...
00101$:
;test.c:257: if( i < j )
ld a,4(ix)
sub a,6(ix)
ld a,5(ix)
sbc a,7(ix)
jp p,00113$
;test.c:259: if( j < k )
ld a,6(ix)
sub a,8(ix)
ld a,7(ix)
sbc a,9(ix)
jp p,00110$
;test.c:261: if( k < l )
;использование предыдущего результата
xor a,a
or a,c
jp z,00106$
;test.c:263: if( l < m )
;очень странный код... не понимаю как работает....
or a,a
;есть свертка перехода!
jp z,00101$
;test.c:264: l += m;
ld a,10(ix)
add a,12(ix)
ld 10(ix),a
ld a,11(ix)
adc a,13(ix)
ld 11(ix),a
jp 00114$
;test.c:266: goto end_1;
; genLabel
00106$:
;test.c:269: k += l;
ld a,8(ix)
add a,10(ix)
ld 8(ix),a
ld a,9(ix)
adc a,11(ix)
ld 9(ix),a
jp 00114$
00110$:
;test.c:273: j += k;
ld a,6(ix)
add a,8(ix)
ld 6(ix),a
ld a,7(ix)
adc a,9(ix)
ld 7(ix),a
;test.c:275: goto beg_1;
jp 00101$
00113$:
;test.c:279: i += j;
ld a,4(ix)
add a,6(ix)
ld 4(ix),a
ld a,5(ix)
adc a,7(ix)
ld 5(ix),a
00114$:
;test.c:280: return( i + j + k + l + m );
ld a,4(ix)
add a,6(ix)
ld c,a
ld a,5(ix)
adc a,7(ix)
ld b,a
ld a,c
add a,8(ix)
ld c,a
ld a,b
adc a,9(ix)
ld b,a
ld a,c
add a,10(ix)
ld c,a
ld a,b
adc a,11(ix)
ld b,a
ld l,12(ix)
ld h,13(ix)
add hl,bc
00115$:
pop ix
ret
Последний из имеющихся пациент- z88dk. Порядком помучался пока заставил его генерить исходник ассемблера. Если попросить, вставляет в исходный текст строки на С, но честно предупреждает, что в таком случае некоторая оптимизация не получится. Я буду уточнять, в каких местах возникла разница.
Использовался уровень оптимизации 2
0.Пролог
Его просто нет. Сразу идет код main.
1. Тест на размножение констант и копий.
2.Свертка константКод:;j4 = 2;
ld hl,2 ;const
ld (_j4),hl
;if( i2 < j4 && i4 < j4 )
;нет размножения констант плюс функции для сравнения
ld de,(_i2)
ld hl,(_j4)
call l_lt
jp nc,i_4
ld de,(_i4)
ld hl,(_j4)
call l_lt
;обратите внимание- использование коротких команд
;перехода
;но в то же время систему переходов можно (и нужно)
;было бы оптимизировать
jr c,i_5_i_4
.i_4
jp i_3
.i_5_i_4
;i2 = 2;
ld hl,2 ;const
ld (_i2),hl
;j4 = k5;
.i_3
;та же картина
ld hl,(_k5)
ld (_j4),hl
;if( i2 < j4 && i4 < j4 )
ld de,(_i2)
ld hl,(_j4)
call l_lt
jp nc,i_7
ld de,(_i4)
ld hl,(_j4)
call l_lt
jr c,i_8_i_7
.i_7
jp i_6
.i_8_i_7
;i5 = 3;
ld hl,3 ;const
ld (_i5),hl
.i_6
3.Лишнее присваиваниеКод:;i3 = 1 + 2;
;свернул
ld hl,3 ;const
ld (_i3),hl
;flt_1 = 2.4 + 6.3;
;оп-па! не свернул!
ld hl,i_2+0
call dldpsh
ld hl,i_2+6
call dload
call dadd
ld hl,_flt_1
call dstore
;i2 = 5;
;загрузка константы
ld hl,5 ;const
ld (_i2),hl
;j2 = i + 0;
;0 не стал прибавлять
ld hl,(_i)
ld (_j2),hl
;k2 = i / 1;
;а вот на 1 поделить не побрезговал....
ld hl,(_i)
ld de,1 ;const
ex de,hl
call l_div
ld (_k2),hl
;i4 = i * 1;
;и умножить тоже
ld hl,(_i)
ld de,1
call l_mult
ld (_i4),hl
;i5 = i * 0;
;и на ноль тоже...
ld hl,(_i)
ld de,0
call l_mult
ld (_i5),hl
;printf( "This compiler handles divide-by-zero as an error\n");
;да, он прохавал деление на нулевую константу
;параметры через стек
ld hl,i_1+0
push hl
call _printf
pop bc
;flt_3 = 2.4 / 1.0;
;мрак... на 1 тоже делит, да еще и в реальном времени...
ld hl,i_2+0
call dldpsh
ld hl,i_2+12
call dload
call ddiv
ld hl,_flt_3
call dstore
;flt_4 = 1.0 + 0.0000001;
;без слов...
ld hl,i_2+12
call dldpsh
ld hl,i_2+18
call dload
call dadd
ld hl,_flt_4
call dstore
;flt_5 = flt_6 * 0.0;
;...одни эмоции
ld hl,_flt_6
call dldpsh
ld hl,i_2+24
call dload
call dmul
ld hl,_flt_5
call dstore
;flt_6 = flt_2 * flt_3;
;ну тут по другому никак
ld hl,_flt_2
call dldpsh
ld hl,_flt_3
call dload
call dmul
ld hl,_flt_6
call dstore
4.Снижение мощностиКод:;k3 = 1;
ld hl,1 ;const
ld (_k3),hl
;k3 = 1;
;в худших традициях...
ld hl,1 ;const
ld (_k3),hl
5.Простой циклКод:;k2 = 4 * j5;
;не заметил возможности умножения "малой кровью"
ld hl,(_j5)
ld de,4
call l_mult
ld (_k2),hl
;for( i = 0; i <= 5; i++ )
;реализация цикла for в лучших традициях!
ld hl,0 ;const
ld (_i),hl
jp i_11
.i_9
;ivector4[ i ] = i * 2;
;хорошо что хоть догадался сдвигами умножать на размер
;типа, а не функциями, но лишние операции со стеком
ld hl,_ivector4
push hl
ld hl,(_i)
add hl,hl
pop de
add hl,de
push hl
ld hl,(_i)
add hl,hl
pop de
call l_pint
;классика!
ld hl,(_i)
inc hl
ld (_i),hl
dec hl
.i_11
;мрак...
ld hl,(_i)
ld de,5 ;const
ex de,hl
call l_le
jp c,i_9
.i_10
6.Управление переменной индукции циклаКод:;j5 = 0;
ld hl,0 ;const
ld (_j5),hl
;k5 = 10000;
ld hl,10000 ;const
ld (_k5),hl
;do {
.i_14
;k5 = k5 - 1;
;распознал вычитание единицы!
ld hl,(_k5)
dec hl
ld (_k5),hl
;j5 = j5 + 1;
;и ее прибавление
ld hl,(_j5)
inc hl
ld (_j5),hl
;i5 = (k5 * 3) / (j5 * 5);
;ну ведь можешь же умножать когда хочешь!
ld hl,(_k5)
ld b,h
ld c,l
add hl,bc
add hl,bc
push hl
ld hl,(_j5)
ld b,h
ld c,l
add hl,hl
add hl,hl
add hl,bc
pop de
call l_div
ld (_i5),hl
;} while ( k5 > 0 );
.i_12
;оригинальная проверка на конец цикла
ld hl,(_k5)
xor a
or h
jp m,i_13
or l
jp nz,i_14
.i_13
7.Глубокие подвыраженияКод:;for( i = 0; i < 100; i++ )
ld hl,0 ;const
ld (_i),hl
jp i_17
.i_15
;ivector5[ i * 2 + 3 ] = 5;
;специфично, но в целом терпимо
ld hl,_ivector5
push hl
ld hl,(_i)
add hl,hl
inc hl
inc hl
inc hl
add hl,hl
pop de
add hl,de
ld (hl),#(5 % 256)
inc hl
ld (hl),#(5 / 256)
ld hl,(_i)
inc hl
ld (_i),hl
;а это зачем???
dec hl
.i_17
;оптимизацию for не доделали...
ld hl,(_i)
ld de,100 ;const
ex de,hl
call l_lt
jp c,i_15
.i_16
8.Генерация адреса переменной с константным индексом, размножение копий и регистровКод:;if( i < 10 )
;хреново сравнивает!
ld hl,(_i)
ld de,10 ;const
ex de,hl
call l_lt
jp nc,i_18
;j5 = i5 + i2;
;зато хоть складывает нормально
ld de,(_i5)
ld hl,(_i2)
add hl,de
ld (_j5),hl
;else
jp i_19
.i_18
;k5 = i5 + i2;
;выделения подвыражений нет
ld de,(_i5)
ld hl,(_i2)
add hl,de
ld (_k5),hl
.i_19
9.Удаление общих подвыраженийКод:;ivector[ 0 ] = 1;
;правильно
ld hl,1 ;const
ld (_ivector),hl
;ivector[ i2 ] = 2;
;лишние операции со стеком, простая перестановка дала
;бы прирост эффективности
ld hl,_ivector
push hl
ld hl,(_i2)
add hl,hl
pop de
add hl,de
ld de,2 ;const
ex de,hl
;да что за мрак!
call l_pint
;ivector[ i2 ] = 2;
;не воспользовался результатом предыдущей операции
ld hl,_ivector
push hl
ld hl,(_i2)
add hl,hl
pop de
add hl,de
ld de,2 ;const
ex de,hl
call l_pint
;ivector[ 2 ] = 3;
;тут все правильно
ld hl,3 ;const
ld (_ivector+4),hl
10.Вынесение инвариантного кодаКод:;if(( h3 + k3 ) < 0 || ( h3 + k3 ) > 5 )
;хорошее начало, но дальше не очень
ld de,(_h3)
ld hl,(_k3)
add hl,de
ld de,0 ;const
ex de,hl
call l_lt
jp c,i_21
;не используются результаты...
ld de,(_h3)
ld hl,(_k3)
add hl,de
ld de,5 ;const
ex de,hl
call l_gt
jp nc,i_20
.i_21
;printf("Common subexpression elimination\n");
;параметры через стек
ld hl,i_1+63
push hl
call _printf
pop bc
;else {
jp i_23
.i_20
;m3 = ( h3 + k3 ) / i3;
;совсем не используются...
ld de,(_h3)
ld hl,(_k3)
add hl,de
ex de,hl
ld hl,(_i3)
call l_div
ld (_m3),hl
;g3 = i3 + (h3 + k3);
ld hl,(_i3)
push hl
ld de,(_h3)
ld hl,(_k3)
add hl,de
pop de
add hl,de
ld (_g3),hl
;}
.i_23
Код:;for( i4 = 0; i4 <= 2; i4++)
ld hl,0 ;const
ld (_i4),hl
jp i_26
.i_24
;ivector2[ i4 ] = j * k;
;обычная обработка индекса массива
ld de,_ivector2
ld hl,(_i4)
add hl,de
push hl
;но вынесения кода нет
ld de,(_j)
ld hl,(_k)
call l_mult
pop de
ld a,l
ld (de),a
ld hl,(_i4)
inc hl
ld (_i4),hl
dec hl
.i_26
ld hl,(_i4)
ld de,2 ;const
ex de,hl
call l_le
jp c,i_24
.i_25
11.Вызов функции с аргументами. Определение недостижимого кода
Код:;dead_code( 1, "This line should not be printed" );
;параметры через стек, слева направо (хотя должно быть наоборот...)
ld hl,1 ;const
push hl
ld hl,i_1+97
push hl
call _dead_code
pop bc
pop bc
12.Вызов функции без аргументов. Исключение цикловКод:;void dead_code( a, b )
;int a;
;char *b;
._dead_code
;{
;int idead_store;
;idead_store = a;
ld hl,6-2 ;const
add hl,sp
call l_gint ;
push hl
;if( 0 )
;переход выполняет
jp i_27
;printf( "%s\n", b );
;но код для пустышки генерит!
ld hl,i_1+129
push hl
ld hl,6 ;const
add hl,sp
call l_gint ;
push hl
call _printf
pop bc
pop bc
;}
.i_27
pop bc
ret
Код:call _unnecessary_loop
13. Слияние заголовков цикловКод:;void unnecessary_loop()
;{
._unnecessary_loop
;int x;
;x = 0;
;регистровая переменная- хорошо
ld hl,0 ;const
;но в стеке- плохо
push hl
;for( i = 0; i < 5; i++ )
ld hl,0 ;const
ld (_i),hl
jp i_30
.i_28
;k5 = x + j5;
;прикольно :)
pop de
push de
ld hl,(_j5)
add hl,de
ld (_k5),hl
ld hl,(_i)
inc hl
ld (_i),hl
dec hl
.i_30
ld hl,(_i)
ld de,5 ;const
ex de,hl
call l_lt
jp c,i_28
.i_29
;}
pop bc
ret
14.Разворачивание цикловКод:;void loop_jamming( x )
;int x;
._loop_jamming
;{
;for( i = 0; i < 5; i++ )
ld hl,0 ;const
ld (_i),hl
jp i_33
.i_31
;k5 = x + j5 * i;
;наверное ошибка- на стеке на один параметр меньше
pop bc
pop hl
push hl
push bc
push hl
ld de,(_j5)
ld hl,(_i)
call l_mult
pop de
add hl,de
ld (_k5),hl
ld hl,(_i)
inc hl
ld (_i),hl
dec hl
.i_33
ld hl,(_i)
ld de,5 ;const
ex de,hl
call l_lt
jp c,i_31
.i_32
;for( i = 0; i < 5; i++ )
;второй цикл точная копия первого
ld hl,0 ;const
ld (_i),hl
jp i_36
.i_34
;i5 = x * k5 * i;
pop bc
pop hl
push hl
push bc
ex de,hl
ld hl,(_k5)
call l_mult
ex de,hl
ld hl,(_i)
call l_mult
ld (_i5),hl
ld hl,(_i)
inc hl
ld (_i),hl
dec hl
.i_36
ld hl,(_i)
ld de,5 ;const
ex de,hl
call l_lt
jp c,i_34
.i_35
;}
ret
15.Сжатие цепочки переходовКод:;void loop_unrolling( x )
;int x;
._loop_unrolling
;{
;for( i = 0; i < 6; i++ )
ld hl,0 ;const
ld (_i),hl
jp i_39
.i_37
;ivector4[ i ] = 0;
;нет разворота, да и загрузка достаточно тяжела
ld hl,_ivector4
push hl
ld hl,(_i)
add hl,hl
pop de
add hl,de
ld (hl),#(0 % 256)
inc hl
ld (hl),#(0 / 256)
ld hl,(_i)
inc hl
ld (_i),hl
dec hl
.i_39
ld hl,(_i)
ld de,6 ;const
ex de,hl
call l_lt
jp c,i_37
.i_38
;}
ret
Итоговая оценка- 5/10. Туповатый компилятор, на который изредка находит озарение и он генерит неплохой код. К применению не рекомендуется...Код:;int jump_compression( i, j, k, l, m )
;int i, j, k, l, m;
._jump_compression
;{
;beg_1:
.i_40
;if( i < j )
;вот тут бы не помешали индексные регистры, да не
;используются совсем...
ld hl,10 ;const
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,10 ;const
add hl,sp
call l_gint ;
pop de
call l_lt
jp nc,i_41
;{
;if( j < k )
ld hl,8 ;const
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,8 ;const
add hl,sp
call l_gint ;
pop de
call l_lt
jp nc,i_42
;{
;if( k < l )
ld hl,6 ;const
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,6 ;const
add hl,sp
call l_gint ;
pop de
call l_lt
jp nc,i_43
;{
;if( l < m )
ld hl,4 ;const
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,4 ;const
add hl,sp
call l_gint ;
pop de
call l_lt
jp nc,i_44
;l += m;
ld hl,4 ;const
add hl,sp
push hl
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,6 ;const
add hl,sp
call l_gint ;
pop de
add hl,de
pop de
call l_pint
;else
jp i_45
.i_44
;goto end_1;
;нет оптимизации переходов
jp i_46
.i_45
;}
;else
jp i_47
.i_43
;k += l;
ld hl,6 ;const
add hl,sp
push hl
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,8 ;const
add hl,sp
call l_gint ;
pop de
add hl,de
pop de
call l_pint
.i_47
;}
;else
jp i_48
.i_42
;{
;j += k;
ld hl,8 ;const
add hl,sp
push hl
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,10 ;const
add hl,sp
call l_gint ;
pop de
add hl,de
pop de
call l_pint
;end_1:
.i_46
.i_49
;goto beg_1;
jp i_40
;}
.i_48
;}
;else
jp i_50
.i_41
;i += j;
ld hl,10 ;const
add hl,sp
push hl
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,12 ;const
add hl,sp
call l_gint ;
pop de
add hl,de
pop de
call l_pint
.i_50
;return( i + j + k + l + m );
ld hl,10 ;const
add hl,sp
ld e,(hl)
inc hl
ld d,(hl)
push de
ld hl,10 ;const
add hl,sp
call l_gint ;
pop de
add hl,de
ex de,hl
ld hl,8-2 ;const
add hl,sp
call l_gint ;
add hl,de
ex de,hl
ld hl,6-2 ;const
add hl,sp
call l_gint ;
add hl,de
ex de,hl
ld hl,4-2 ;const
add hl,sp
call l_gint ;
add hl,de
ret
;}
Спасибо, интересное исследование. Значит sdcc... Но вот меня смущает, что на sdcc.sf.net постоянно упоминается "freeware", несмотря на его GPL'ность.
А по поводу переменных, наверное в реальных проектах будет много глобальных статических, чтобы компилятор их просто клал в память, не извращаясь со стеком или IX/IY.