В том случае это не просто было сворачивание константных подвыражений. Это было сворачивание параметра функции до константы, раз он передавался только константой. Согласитесь, это несколько другое.
В том случае это не просто было сворачивание константных подвыражений. Это было сворачивание параметра функции до константы, раз он передавался только константой. Согласитесь, это несколько другое.
Категорически не соглашусь. Аргумент функции в C -- ровно такое же выражение, как и любое другое. С точки зрения компилятора, между этими двумя вызовами функции нет никакой разницы:
Точнее, разница только в том, что значение переменной a может потребоваться сохранить.Код:int a = 5 + 8; func(a) ... func(5+8)
- - - Добавлено - - -
Или речь о том, что он сворачивает вызов чистых функций с константными аргументами в предвычисленную константу? Так это тоже оптимизация константных подвыражений. Чистые функции по определению всегда возвращают одно и то же при тех же аргументах.
Имеется в виду вот что:
Насколько говорит мой опыт, компиляторы Си (особенно набортные или для ретро-таргетов) эту оптимизацию не делают.Код:void func (int a, int b, int с) { ... a ; // вот здесь компилятор прямо вставляет константу вместо a, // ибо он проверил, что все вызовы func идут с этой константой }
А, понял. На самом деле, такая частичная параметризация возможна не всегда. Если мы экспортируем функцию, то компилятор не имеет права так делать, поскольку мало ли кто её с какими параметрами будет вызывать.
Что же касается GCC, то, видимо, это косвенный эффект оптимизации шаблонов с константными параметрами, типа MyTemplate<a, 5>. В STL и Boost это просто необходимо, там такого -- вагон и маленькая тележка. Страшный язык этот современный C++, вот что я скажу.
А вот в C компиляторах я действительно никогда такого не видел.
Думаю, если эту фекцию экспортировать, то ничего подобного не будет, скорее всего компилятор даже не попытается порождать специализированную версию. Лень проверять![]()
Видимо, да. Функция не экспортирована, помечена static, потому и возможна эта оптимизация. Но GCC реально удивляет качеством кода и помимо этого случая.
жаль, что godbolt не умеет в pdp11
любопытно посмотреть, во что развернется такой код
Код:void update(unsigned char *mem, int *bitmap, int x, int y) { for (int m = x; m < x+y; m++) { unsigned char bits = *mem++; for (int i = 0; i < 8; i++) { *bitmap++ = (bits >> i) & 1; } } }
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
GCC даёт такой код:
Код:; --------------------------------------------------------------------------- mov R2, -(SP) mov R3, -(SP) mov R4, -(SP) mov R5, -(SP) add #-12, SP mov 30(SP), R3 clr R2 mov 32(SP), R0 add R3, R0 mov 32(SP), R1 inc R1 cmp R3, R0 bgt loc_1062 cmp R0, #-100000 bne loc_1066 loc_1062: ; CODE XREF: RAM:001052↑j mov #1, R1 loc_1066: ; CODE XREF: RAM:001060↑j ; RAM:001242↓j mov #4, R0 mov R2, R3 loc_1074: ; CODE XREF: RAM:001100↓j asl R3 dec R0 bne loc_1074 add 26(SP), R3 mov R3, 10(SP) dec R1 beq loc_1122 jmp loc_1140 ; --------------------------------------------------------------------------- loc_1122: ; CODE XREF: RAM:001114↑j add #12, SP mov (SP)+, R5 mov (SP)+, R4 mov (SP)+, R3 mov (SP)+, R2 return ; --------------------------------------------------------------------------- loc_1140: ; CODE XREF: RAM:001116↑J mov 24(SP), R0 add R2, R0 movb @R0, 7(SP) clr R0 mov #10, R3 loc_1160: ; CODE XREF: RAM:001236↓j mov R0, R4 asl R4 add 10(SP), R4 mov R4, 2(SP) clr R4 bisb 7(SP), R4 mov R4, @SP mov R0, R5 ble loc_1220 loc_1210: ; CODE XREF: RAM:001214↓j asr R4 dec R5 bne loc_1210 mov R4, @SP loc_1220: ; CODE XREF: RAM:001206↑j mov @SP, R4 bic #-2, R4 mov 2(SP), R5 mov R4, @R5 inc R0 sob R3, loc_1160 inc R2 br loc_1066 ; ---------------------------------------------------------------------------
Печально. Оптимизатор не осилил.
Вот, например:
Очевидные ошибки:Код:loc_1066: ; CODE XREF: RAM:001060↑j ; RAM:001242↓j mov #4, R0 mov R2, R3 loc_1074: ; CODE XREF: RAM:001100↓j asl R3 dec R0 bne loc_1074
1) Проигнорировано существование команды SOB. Компилятор прибит гвоздями к самым младшим моделям PDP-11? Не верится, дальше-то SOB есть.
2) Правильно этот код пишется так:
На слово короче, не использует дополнительный регистр и как минимум в 3 раза быстрее.Код:mov R2, R3 asl R3 asl R3 asl R3 asl R3
3) Чудовищным образом закодирован цикл со счётом вниз до нуля. Ну это же типовая идиома, кодогенератор должен её узнавать и вставлять шаблон.
А самое главное -- это не совсем от этой функции код :P
Вот сходу в уме декомпилировал начало функции:
- - - Добавлено - - -Код:func(int a, int b, int c, int d) { int v3 = c; int v2 = c + d; int v1 = d +1 // ЧТО ЭТО ??? if (v3 > v0 || v0 == 0100000) { v3 = v2* 16; } while (--v1 > 0) { ... } }
А в этом коде ничего, случаем, не пропало? Что там происходит с x? Зачем он нужен?
- - - Добавлено - - -
Переписал вручную на ассемблере. Если x всё-таки имеет значение, то в инициализации цикла добавляется ещё несколько команд.
Аргументы в регистрах r1..r4
- - - Добавлено - - -Код:;void update(unsigned char *mem, int *bitmap, int x, int y) { ; for (int m = x; m < x+y; m++) { ; unsigned char bits = *mem++; ; for (int i = 0; i < 8; i++) { ; *bitmap++ = (bits >> i) & 1; ; } ; } ;} update: mov r5, -(sp) mov r4, -(sp) mov r3, -(sp) mov r2, -(sp) mov r1, -(sp) cmp #0, r4 bge 0$ 1$: movb (r1)+, r0 mov #10, r5 2$: clr r3 asr r0 adc r3 mov r3, (r2)+ sob r5, 2$ sob r4, 1$ 0$: mov (sp)+, r1 mov (sp)+, r2 mov (sp)+, r3 mov (sp)+, r4 mov (sp)+, r5 ret
Таки залез на gobolt.org, и проверил, что тамошняя коллекция компялиторов думает.
Несколько родственный MSP-430
Ужас какой! Плохо у GCC с 16-битками...Код:update: PUSHM.W #1, R10 SUB.W #14, R1 MOV.W R12, 6(R1) MOV.W R13, 4(R1) MOV.W R14, 2(R1) MOV.W R15, @R1 MOV.W 2(R1), 12(R1) BR #.L2 .L5: MOV.W 6(R1), R12 MOV.W R12, R13 ADD.W #1, R13 MOV.W R13, 6(R1) MOV.B @R12, 9(R1) MOV.W #0, 10(R1) BR #.L3 .L4: MOV.W 4(R1), R10 MOV.W R10, R12 ADD.W #2, R12 MOV.W R12, 4(R1) MOV.B 9(R1), R12 MOV.W 10(R1), R13 CALL #__mspabi_srai AND.B #1, R12 MOV.W R12, @R10 ADD.W #1, 10(R1) .L3: MOV.B #7, R12 CMP.W 10(R1), R12 { JGE .L4 ADD.W #1, 12(R1) .L2: MOV.W 2(R1), R12 ADD.W @R1, R12 CMP.W R12, 12(R1) { JL .L5 NOP ADD.W #14, R1 POPM.W #1, r10 RET
Богомерзкий RISC-V:
А вот это примерно то же самое, что и я написал.Код:update(unsigned char*, int*, int, int): add a5,a2,a3 add a7,a0,a3 li a6,8 ble a5,a2,.L1 .L5: lbu a2,0(a0) mv a3,a1 addi a0,a0,1 li a5,0 .L3: sra a4,a2,a5 addi a3,a3,4 andi a4,a4,1 sw a4,-4(a3) addi a5,a5,1 bne a5,a6,.L3 addi a1,a1,32 bne a7,a0,.L5 .L1: ret
изначально этот код выглядел примерно так:
https://github.com/mamedev/mame/blob...an240.cpp#L424
Код:u32 okean240_state::screen_update_okean240(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { for (u16 y = 0; y < 256; y++) { u8 const ma = y + m_scroll; // ma must be 8-bit u16 *p = &bitmap.pix(y); for (u16 x = 0; x < 0x4000; x+=0x200) { u8 const gfx = m_p_videoram[x|ma] | m_p_videoram[x|ma|0x100]; /* Display a scanline of a character */ *p++ = BIT(gfx, 0); *p++ = BIT(gfx, 1); *p++ = BIT(gfx, 2); *p++ = BIT(gfx, 3); *p++ = BIT(gfx, 4); *p++ = BIT(gfx, 5); *p++ = BIT(gfx, 6); *p++ = BIT(gfx, 7); } } return 0; }
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)