SPA эквивалент практичному програмингу - Low Level Developers Network програминг на асме - да для удовольствия - да тяжко и долго - но все таки асм тут самый продвинутый язык тысячелетия а не ЦЭ плус плус без асма этот форум жалкая тень рдсн
SPA я отреверсил (не полностью правда) написанный на ЦЭ NC пришел в ужас и теперь пишу на асм применяя патерны из знаменитой книжки сделал пару обобщений, повысил немного уровень абстракций, вообщем вагон удовольствия в свободное время
Black_mirror похоже ты специально написал такой ужас inline void inc_long(long* const p_long) { char * const p = (char*)p_long; !++p[0] && !++p[1] && !++p[2] && !++p[3]; } inc_long(&long_var); Кончено если у 8ми битки есть флаг переноса то С сливает. Но твой пример скорее исключение, чем правило,
J0E Про inline я не догадался, потому что до этого наблюдал как gcc неправильно встраивал функции, которые были без всяких inline. Но сейчас решил посмотреть во что он это всё превратит. Код (Text): long var; inline void inc_long(long* const p_long) { char * const p = (char*)p_long; !++p[0] && !++p[1] && !++p[2] && !++p[3]; } int main() { while(1) { inc_long(&var); if(!++*((char*)&var)) if(!++*(((char*)&var)+1)) if(!++*(((char*)&var)+2)) ++*(((char*)&var)+3); ++var; } } Обычный инкремент(4 загрузки, 3 сложения, 4 сохранения): Код (Text): ++var; 2ac: 80 91 00 20 lds r24, 0x2000 2b0: 90 91 01 20 lds r25, 0x2001 2b4: a0 91 02 20 lds r26, 0x2002 2b8: b0 91 03 20 lds r27, 0x2003 2bc: 01 96 adiw r24, 0x01 ; 1 2be: a1 1d adc r26, r1 2c0: b1 1d adc r27, r1 2c2: 80 93 00 20 sts 0x2000, r24 2c6: 90 93 01 20 sts 0x2001, r25 2ca: a0 93 02 20 sts 0x2002, r26 2ce: b0 93 03 20 sts 0x2003, r27 В режиме оптимизации по размеру -Os(здесь на каждый байт кроме последнего добавляется бесполезная команда сравнения и команда перехода, то есть в большинстве случаев будет выполняться всего 5 команд вместо 11 в неоптимизировнном варианте. Вариант J0E выглядит абсолютно идентично): Код (Text): if(!++*((char*)&var)) 278: 80 91 00 20 lds r24, 0x2000 27c: 8f 5f subi r24, 0xFF ; 255 27e: 80 93 00 20 sts 0x2000, r24 282: 88 23 and r24, r24 284: 99 f4 brne .+38 ; 0x2ac <main+0x68> if(!++*(((char*)&var)+1)) 286: 80 91 01 20 lds r24, 0x2001 28a: 8f 5f subi r24, 0xFF ; 255 28c: 80 93 01 20 sts 0x2001, r24 290: 88 23 and r24, r24 292: 61 f4 brne .+24 ; 0x2ac <main+0x68> if(!++*(((char*)&var)+2)) 294: 80 91 02 20 lds r24, 0x2002 298: 8f 5f subi r24, 0xFF ; 255 29a: 80 93 02 20 sts 0x2002, r24 29e: 88 23 and r24, r24 2a0: 29 f4 brne .+10 ; 0x2ac <main+0x68> ++*(((char*)&var)+3); 2a2: 80 91 03 20 lds r24, 0x2003 2a6: 8f 5f subi r24, 0xFF ; 255 2a8: 80 93 03 20 sts 0x2003, r24 Что касается других режимов оптимизации, то получаемый код по логике отличается не сильно, если не считать попытки передачи одного байта var для следующего инкремента через регистр, загрузки указателей на каждый байт var в адресные регистры X,Y,Z, встраивание половины inc_long за циклом с последующим обратным прыжком, и некоторых других мелких отжигов отптимизатора связанных с контекстом вызова, а не с самим инкрементом.
Мой вариант и должен быть идентичным, более краткая запись. Оптимизатор как видно тупит, что насчет этого: ++p[0] || ++p[1] || ++p[2] || ++p[3];
Выглядит более дзенско, но and r24, r24 gcc убрать всё равно не может: Код (Text): inline void inc_long(long* const p_long) { char * const p = (char*)p_long; ++p[0] || ++p[1] || ++p[2] || ++p[3]; 244: 80 91 00 20 lds r24, 0x2000 248: 8f 5f subi r24, 0xFF ; 255 24a: 80 93 00 20 sts 0x2000, r24 24e: 88 23 and r24, r24 250: 99 f4 brne .+38 ; 0x278 <main+0x34> 252: 80 91 01 20 lds r24, 0x2001 256: 8f 5f subi r24, 0xFF ; 255 258: 80 93 01 20 sts 0x2001, r24 25c: 88 23 and r24, r24 25e: 61 f4 brne .+24 ; 0x278 <main+0x34> 260: 80 91 02 20 lds r24, 0x2002 264: 8f 5f subi r24, 0xFF ; 255 266: 80 93 02 20 sts 0x2002, r24 26a: 88 23 and r24, r24 26c: 29 f4 brne .+10 ; 0x278 <main+0x34> 26e: 80 91 03 20 lds r24, 0x2003 272: 8f 5f subi r24, 0xFF ; 255 274: 80 93 03 20 sts 0x2003, r24
Видно что GCC на уровне AST проблем не имеет, тупит генератор кода под конкретный проц. Я не знаком с этим набором команд, char по умолчанию signed? Если замена на unsigned ничего не изменит, то дополнительно можно попробовать поменять условие в сравнении на ++p[0] > 0
J0E У компилятора включена директива чтобы char был unsigned. Но сравнение с нулём всё равно ничего не меняет.
итак гемор с цэ плус плус налицо - пусть код написан быстро но очень долго порой бывает опции выставлять что получилось чтонить нормальное
Да с опциями что-то не так, используется оптимизация по размеру, хотя требуется скорость, то есть что бы для инкремента long создавался какой то такой код: lds r24, 0x2000 lds r25, 0x2001 adiw r24, 0x01 ; 1 sts 0x2000, r24 brnс @f ; есть такое ??? выходим если не было переноса. если такой команды нет то and вполне понятен lds r26, 0x2002 lds r27, 0x2003 adc r26, r1 adc r27, r1 sts 0x2001, r25 sts 0x2002, r26 sts 0x2003, r27 @@: Наверное это можно подкрутить где то внутри GCC ) В случае 8ми биток конечно тяжело соревноваться с асмом, тем более С не работает с флагом переноса. Но не будем подменять понятия, я всего-то привел пример более читабельного кода, без претензий на скорость. А про крутить опции для x86 рассказывайте сказки кому то другому )) людям приходится решать реальные задачи а не только писать командер под ДОС.