В том случае это не просто было сворачивание константных подвыражений. Это было сворачивание параметра функции до константы, раз он передавался только константой. Согласитесь, это несколько другое.
Вид для печати
В том случае это не просто было сворачивание константных подвыражений. Это было сворачивание параметра функции до константы, раз он передавался только константой. Согласитесь, это несколько другое.
Категорически не соглашусь. Аргумент функции в 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;
}
}
}
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;
}