Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: mhi 23. 10. 2021, 22:47:53
-
Resim aktualne takovy problem, jehoz vystup asi uplne nepotesi priznivce RISC-V ekosystemu (nebo ukaze nejakou moji neznalost). Pomoci velmi podmineneho prekladu s gcc ( -Os ) vytvarim 'code snippety', ktere nasledne nejaky automat bude skladat za sebe (jde o dynamicky translator). Protoze chci zjistit, jestli to bude pouzitelne i jinde nez v x86 svete, nasbiral jsem ruzne prekladace a zkusil tyto code snippety vygenerovat pro ruzne architektury. Zde je vysledek; pocet vygenerovanych instrukci pro nejakou sadu tech snippetu; cim nizsi, tim kratsi kod. CISC je zde zvyhodnen, protoze instrukce hlavne u x86_64 jsou mnohem delsi nez jinde, hlavne u ARM (thumb2). Co jsem zkoumal vystup, obvykle vic instrukci rovna se i vyrazne mene optimalni kod a zbytecne zonglovani s daty.
10527 report-arm-noflags.txt (thumb2)
10938 report-armv8-noflags.txt
11209 report-x64-noflags.txt
12311 report-ppc32-noflags.txt
12371 report-sparc64-noflags.txt
12809 report-xtensa-noflags.txt
13036 report-x86-noflags.txt
14718 report-mips32-noflags.txt
16903 report-rv32-noflags.txt
18407 report-rv64-noflags.txt
Mam jeste jednu sadu, kde se vyhodnocuji nejake operace, to pridava ale do vetsiny snippetu celkem dost podobneho kodu, ktery by sel resit za pouziti flagu (cf/of/if) kdyby to gcc umelo lepe (typicky pushf a pozdeji v kodu popf pro nasledne snadne zpracovani ruznych overflow/carry/apod.). Spis jen pro priklad vlozim i to:
13151 report-x64-flags.txt
13225 report-arm-flags.txt
15175 report-ppc32-flags.txt
15535 report-x86-flags.txt
15770 report-sparc64-flags.txt
17118 report-xtensa-flags.txt
22410 report-rv32-flags.txt
23900 report-rv64-flags.txt
Krome RV64 a mam i realne zelezo, tak mohu porovnat i vysledky v rychlosti, nejak upravene na nejake MHz procesoru (xtensa je ESP32 LX6, tezko srovnavat primo s PowerPC nebo x86 dospelym pocitacem).
Nicmene mam i otazku. GCC mi generuje kod, ve kterem se skace a rutina nekonci nejakym "ret/blr", priklad (ppc32):
0: 94 21 ff e0 stwu r1,-32(r1)
4: 7c 08 02 a6 mflr r0
8: 90 01 00 24 stw r0,36(r1)
c: 93 e1 00 1c stw r31,28(r1)
..
48: 7d 4a 40 39 and. r10,r10,r8
4c: 41 82 00 18 beq 64
50: 48 00 00 01 bl 50
50: R_PPC_REL24 spec_read
54: 7c 7f 1a 14 add r3,r31,r3
58: 98 7d 00 00 stb r3,0(r29)
5c: 39 61 00 20 addi r11,r1,32
[b] 60: 48 00 00 00 b 60 <exec86+0x60>
60: R_PPC_REL24 _restgpr_31_x (!!!)[/b]
64: 81 3d 00 2c lwz r9,44(r29)
68: 7c 69 18 ae lbzx r3,r9,r3
[b] 6c: 4b ff ff e8 b 54 [/b]
Na adrese 60 je netypicke ukonceni (dusledek -Os), kazdopadne i kdyby tam bylo blr, nekonci tim rutina. Ja bych potreboval, abych z konce rutiny (0x6c) mohl jen odriznout blr a nebyl tam nejaky odskok "nahoru". A tim by slo trivialne retezit snippety za sebe v dynamicky generovanem kodu. Tusite jak k necemu takovemu primet gcc? Nedela to jen na powerpc, ale i jinych architekturach, typicky ARM. Reseni by bylo "linearizovat" kod, aby sel odshora dolu, a neskakal si nekam vzhuru. Jenze to musim naprogramovat. Dale bych uvital, kdybych nejak prekladaci umel rici, aby se treba vyvaroval nejakeho konstruktu, treba nejake relokaci, apod. Diky za pripadne tipy.
Prekvapenim je RISC-V, ktery ciselne, ale i pohledove generuje pomerne sileny kod. Tam, kde ma ARM uz spocitano, se risc-v potaci nekde v tom jak vidlemi prehazovat data... registru ma pritom mnohem vic RISC-V.
-
Reseni by bylo "linearizovat" kod, aby sel odshora dolu, a neskakal si nekam vzhuru. Jenze to musim naprogramovat.
Tak jde prece jenom o nejaky relative jump na konec snippetu ne? :)
Tj. mala editace adresy.. ale napsat match na ten exit op bude asi tezky.
-
-O0 a -Os na GCC produkuje velmi špatnej RV kód, porovnej s -O2 nebo -O3 a bude tam toho házení vidlema podstatně míň (a výstup bude paradoxně kratší než s -Os).
-
RDa: pro RISC neni az tak velky problem napsat ten tool, ktery to reorganizuje. Tim ten koncovy blr/ret/jmp/jb/... vzdy "vypadne", resp. bude se s nim dat snadno pracovat.
KS: Pokud na -Os zpravidla generuje vetsi kod nez na -O2/3, tak je toolchain z meho pohledu rozbity. Vyzkousel jsem to, skutecne je kod mensi, ale stale jsme na cislech, ktera jsou neprijatelna:
16695 report-rv32-noflags-O2.txt
16903 report-rv32-noflags-O3.txt (!pocet instrukci stejny jak -Os, ale je to jen nejaka nahoda, kod se lisi)
Ve vysledku uz je zahrnuta nejaka rucni optimalizace C kodu, kterou jsem v mezidobi udelal, na puvodnim kodu by to bylo horsi.
Pro srovnani, arm a x86 jsou na tom nyni takto:
8999 report-arm-noflags.txt thumb2
10715 report.txt toto je X86 noflags, pozor nektere instrukce jsou dost dlouhe
Navyseni velikosti kodu o > 70% znamena, ze i kdyz budu mit stejne efektivni CPU, musim mit treba o 70% vice cache, aby byl stejny vysledek u RISC-V. Navic ma rv32 spousti instrukci 32bitovych, proti arm-thumb2, kde jich je radove mene. x86 ma prumer tak 3-4 byty/instr. Je to pro mne celkem zklamani, na to jak tu nekteri aktualni stav teto ISA obhajuji.
-
Navyseni velikosti kodu o > 70% znamena, ze i kdyz budu mit stejne efektivni CPU, musim mit treba o 70% vice cache, aby byl stejny vysledek u RISC-V. Navic ma rv32 spousti instrukci 32bitovych, proti arm-thumb2, kde jich je radove mene. x86 ma prumer tak 3-4 byty/instr. Je to pro mne celkem zklamani, na to jak tu nekteri aktualni stav teto ISA obhajuji.
Nema RV32 a RV64 fixni velikost instrukce typu 4B*N a 8B*N ? Treba u PPC me hodne prekvapilo (po x86 svete), ze instrukce jsou stejne dlouhe, kdyz jsem videl nejaky disassembly report.
Muzes dat nejaky priklad jednoducheho snippetu, jak vypada na x86/x64, arm/aarch4 a na RV32/RV64 v disasm tvaru?
-
Zajímavé hraní :-) paradoxně mi tu RV64 vychází jako nejkratší. Krátká aritmetická funkce:
fn is_prime(n: u32) bool {
var i: u32 = 2;
while (i <= n / 2) {
if (n % i == 0) {
return false;
}
i += 1;
}
return true;
}
Chvíli jsem musel přemýšlet, protože v jednoduchém programu, kdy jen funkci zavolám z mainu s nějakým parametrem, mi Zig během kompilace dokázal, že jsou všechny argumenty známé a tak funkci vůbec nezkompiloval, rovnou dosadil výsledek :-)
RV32 mi chybí, ve standardní knihovně není implementace jednoho syscallu.
zig build-exe -O ReleaseSmall main.zig:
0000000000208c36 <is_prime>:
208c36: 89 f9 mov ecx, edi
208c38: d1 e9 shr ecx
208c3a: 6a 02 push 2
208c3c: 58 pop rax
208c3d: 89 c6 mov esi, eax
208c3f: 39 c8 cmp eax, ecx
208c41: 77 0d ja 0x208c50 <is_prime+0x1a>
208c43: 89 f8 mov eax, edi
208c45: 31 d2 xor edx, edx
208c47: f7 f6 div esi
208c49: 8d 46 01 lea eax, [rsi + 1]
208c4c: 85 d2 test edx, edx
208c4e: 75 ed jne 0x208c3d <is_prime+0x7>
208c50: 39 ce cmp esi, ecx
208c52: 0f 97 c0 seta al
208c55: c3 ret
zig build-exe -O ReleaseSmall main.zig -target i386-linux:
004087b1 <is_prime>:
4087b1: 57 push edi
4087b2: 56 push esi
4087b3: 89 ce mov esi, ecx
4087b5: d1 ee shr esi
4087b7: 6a 02 push 2
4087b9: 58 pop eax
4087ba: 89 c7 mov edi, eax
4087bc: 39 f0 cmp eax, esi
4087be: 77 0d ja 0x4087cd <is_prime+0x1c>
4087c0: 89 c8 mov eax, ecx
4087c2: 31 d2 xor edx, edx
4087c4: f7 f7 div edi
4087c6: 8d 47 01 lea eax, [edi + 1]
4087c9: 85 d2 test edx, edx
4087cb: 75 ed jne 0x4087ba <is_prime+0x9>
4087cd: 39 f7 cmp edi, esi
4087cf: 0f 97 c0 seta al
4087d2: 5e pop esi
4087d3: 5f pop edi
4087d4: c3 ret
zig build-exe -O ReleaseSmall main.zig -target arm-linux:
00028100 <is_prime>:
28100: 70 40 2d e9 push {r4, r5, r6, lr}
28104: 00 40 a0 e1 mov r4, r0
28108: a0 60 a0 e1 lsr r6, r0, #1
2810c: 02 10 a0 e3 mov r1, #2
28110: 01 50 a0 e1 mov r5, r1
28114: 06 00 51 e1 cmp r1, r6
28118: 05 00 00 8a bhi 0x28134 <is_prime+0x34> @ imm = #20
2811c: 04 00 a0 e1 mov r0, r4
28120: 05 10 a0 e1 mov r1, r5
28124: eb 02 00 eb bl 0x28cd8 <__umodsi3> @ imm = #2988
28128: 01 10 85 e2 add r1, r5, #1
2812c: 00 00 50 e3 cmp r0, #0
28130: f6 ff ff 1a bne 0x28110 <is_prime+0x10> @ imm = #-40
28134: 00 00 a0 e3 mov r0, #0
28138: 06 00 55 e1 cmp r5, r6
2813c: 01 00 00 83 movwhi r0, #1
28140: 70 80 bd e8 pop {r4, r5, r6, pc}
zig build-exe -O ReleaseSmall main.zig -target aarch64-linux:
0000000000216a94 <is_prime>:
216a94: 08 7c 01 53 lsr w8, w0, #1
216a98: 4a 00 80 52 mov w10, #2
216a9c: e9 03 0a 2a mov w9, w10
216aa0: 5f 01 08 6b cmp w10, w8
216aa4: a8 00 00 54 b.hi 0x216ab8 <is_prime+0x24>
216aa8: 0a 08 c9 1a udiv w10, w0, w9
216aac: 4b 81 09 1b msub w11, w10, w9, w0
216ab0: 2a 05 00 11 add w10, w9, #1 // =1
216ab4: 4b ff ff 35 cbnz w11, 0x216a9c <is_prime+0x8>
216ab8: 3f 01 08 6b cmp w9, w8
216abc: e0 97 9f 1a cset w0, hi
216ac0: c0 03 5f d6 ret
zig build-exe -O ReleaseSmall main.zig -target riscv64-linux:
000000000001778c <is_prime>:
1778c: 9b 55 15 00 srliw a1, a0, 1
17790: 09 46 addi a2, zero, 2
17792: 9b 06 06 00 sext.w a3, a2
17796: 63 e6 d5 00 bltu a1, a3, 0x177a2 <is_prime+0x16>
1779a: 3b 77 c5 02 remuw a4, a0, a2
1779e: 05 06 addi a2, a2, 1
177a0: 6d fb bnez a4, 0x17792 <is_prime+0x6>
177a2: 33 b5 d5 00 sltu a0, a1, a3
177a6: 82 80 ret
zig build-exe -O ReleaseSmall main.zig -target powerpc-linux:
10018e9c <is_prime>:
10018e9c: 54 64 f8 7e srwi 4, 3, 1
10018ea0: 38 c4 00 01 addi 6, 4, 1
10018ea4: 38 a0 00 02 li 5, 2
10018ea8: 28 06 00 02 cmplwi 6, 2
10018eac: 41 81 00 0c bt 1, 0x10018eb8
10018eb0: 60 a6 00 00 ori 6, 5, 0
10018eb4: 48 00 00 04 b 0x10018eb8
10018eb8: 38 c6 ff ff addi 6, 6, -1
10018ebc: 7c c9 03 a6 mtctr 6
10018ec0: 7c a6 2b 78 mr 6, 5
10018ec4: 42 40 00 18 bdz 0x10018edc
10018ec8: 7c a3 33 96 divwu 5, 3, 6
10018ecc: 7c a5 31 d6 mullw 5, 5, 6
10018ed0: 7c a5 18 51 sub. 5, 3, 5
10018ed4: 38 a6 00 01 addi 5, 6, 1
10018ed8: 40 82 ff e8 bf 2, 0x10018ec0
10018edc: 7c 06 20 40 cmplw 6, 4
10018ee0: 38 60 00 00 li 3, 0
10018ee4: 38 80 00 01 li 4, 1
10018ee8: 41 81 00 08 bt 1, 0x10018ef0
10018eec: 4e 80 00 20 blr
10018ef0: 38 64 00 00 addi 3, 4, 0
10018ef4: 4e 80 00 20 blr
zig build-exe -O ReleaseSmall main.zig -target mips-linux:
00029adc <is_prime>:
29adc: 00 04 10 42 srl $2, $4, 1 <main+0x1>
29ae0: 24 03 00 02 addiu $3, $zero, 2 <main+0x2>
29ae4: 24 06 00 02 addiu $6, $zero, 2 <main+0x2>
29ae8: 24 41 00 01 addiu $1, $2, 1 <main+0x1>
29aec: 2c 25 00 03 sltiu $5, $1, 3 <main+0x3>
29af0: 00 25 18 0a movz $3, $1, $5
29af4: 10 66 00 07 beq $3, $6, 32 <is_prime+0x38>
29af8: 00 c0 28 25 move $5, $6
29afc: 00 85 00 1b divu $zero, $4, $5
29b00: 24 a6 00 01 addiu $6, $5, 1 <main+0x1>
29b04: 00 a0 01 f4 teq $5, $zero, 7 <main+0x7>
29b08: 00 00 08 10 mfhi $1
29b0c: 14 20 ff f9 bnez $1, -24 <is_prime+0x18>
29b10: 00 00 00 00 nop <main>
29b14: 03 e0 00 08 jr $ra
29b18: 00 45 10 2b sltu $2, $2, $5
-
Když to přepíšu do C a nechám zkompilovat zigem (jde to přes libclang a llvm), tak je ta funkce v RV64 podstatně ukecanější, ale neumím určit, jestli to je jiným nastavením optimalizace (ReleaseSmall vs. -Os), nebo se v Zigu se silnějším typovým systémem daří lépe hledat invarianty.
zig cc main.c -target riscv64-linux -Os -o main:
0000000000000000 <is_prime>:
0: 11 46 addi a2, zero, 4
2: 85 45 addi a1, zero, 1
4: 63 64 c5 02 bltu a0, a2, 0x2c <is_prime+0x2c>
8: 9b 55 15 00 srliw a1, a0, 1
c: 09 46 addi a2, zero, 2
e: bb 76 c5 02 remuw a3, a0, a2
12: 1b 07 06 00 sext.w a4, a2
16: b3 37 d0 00 snez a5, a3
1a: 33 37 b7 00 sltu a4, a4, a1
1e: 7d 8f and a4, a4, a5
20: 05 06 addi a2, a2, 1
22: 75 f7 bnez a4, 0xe <is_prime+0xe>
24: 13 b5 16 00 seqz a0, a3
28: 93 45 15 00 xori a1, a0, 1
2c: 2e 85 mv a0, a1
2e: 82 80 ret
-
KS: Evidentne programujete 'v jinem svete' nez ja :), ty moje rutiny nejsou takhle primocare. Vezmeme tento ARM kod, ktery dela nejakou primitivni operaci s pameti - u toho ale zjistuje pres tabulky jestli ma do pameti pristup (cteni i zapis). Na kodu je videt, ze se tam opakuje vlastne 3x vypocet, ktery gcc neumelo eliminovat, nektere vysledky by sly pouzit opakovane, ale pocitaji se porad znovu. Ten kod je opravdu neoptimalni a hnusny, s tim s bude muset neco udelat. Gcc to dela uplne vsude, x86/x64/arm/aarch64/ppc/riscv/xtensa/... ; JENZE na RISC-V ma tento kod vice nez dvojnasobnou delku na instrukce i delku (nebudu to sem vkladat, je to fakt nuda). Musim vyzkouset jestli primeju gcc pres nejake pomocne promenne ty mezivypocty recyklovat.
Zdrojak sem hodit nemuzu, ma to 3000 radek s hromadou maker a #ifdefu.
0: 89ba ldrh r2, [r7, #12]
2: 88f8 ldrh r0, [r7, #6]
4: 8afb ldrh r3, [r7, #22]
6: 4410 add r0, r2
8: 6a7a ldr r2, [r7, #36] ; 0x24
a: b570 push {r4, r5, r6, lr}
c: b280 uxth r0, r0
e: eb00 1003 add.w r0, r0, r3, lsl #4
12: 011d lsls r5, r3, #4
14: f3c0 3404 ubfx r4, r0, #12, #5
18: 2301 movs r3, #1
1a: 0c41 lsrs r1, r0, #17
1c: 40a3 lsls r3, r4
1e: f852 2021 ldr.w r2, [r2, r1, lsl #2]
22: 4213 tst r3, r2
24: d105 bne.n 32 <rtn11+0x32>
26: f3c0 020b ubfx r2, r0, #0, #12
2a: f640 73ff movw r3, #4095 ; 0xfff
2e: 429a cmp r2, r3
30: d129 bne.n 86 <rtn11+0x86>
32: f7ff fffe bl 0 <spec_rdmem_word>
32: R_ARM_THM_CALL spec_rdmem_word
36: 893b ldrh r3, [r7, #8]
38: 7e39 ldrb r1, [r7, #24]
3a: 88fc ldrh r4, [r7, #6]
3c: 4419 add r1, r3
3e: 89bb ldrh r3, [r7, #12]
40: 4408 add r0, r1
42: 6aba ldr r2, [r7, #40] ; 0x28
44: 441c add r4, r3
46: 2301 movs r3, #1
48: b286 uxth r6, r0
4a: b2c1 uxtb r1, r0
4c: fa15 f484 uxtah r4, r5, r4
50: f3c4 3504 ubfx r5, r4, #12, #5
54: 0c60 lsrs r0, r4, #17
56: 40ab lsls r3, r5
58: f852 2020 ldr.w r2, [r2, r0, lsl #2]
5c: 4213 tst r3, r2
5e: d015 beq.n 8c <rtn11+0x8c>
60: 4620 mov r0, r4
62: f7ff fffe bl 0 <spec_wrmem>
62: R_ARM_THM_CALL spec_wrmem
66: 1c60 adds r0, r4, #1
68: 6aba ldr r2, [r7, #40] ; 0x28
6a: f3c0 3504 ubfx r5, r0, #12, #5
6e: 2301 movs r3, #1
70: 0c44 lsrs r4, r0, #17
72: 40ab lsls r3, r5
74: 0a31 lsrs r1, r6, #8
76: f852 2024 ldr.w r2, [r2, r4, lsl #2]
7a: 4213 tst r3, r2
7c: d009 beq.n 92 <rtn11+0x92>
7e: e8bd 4070 ldmia.w sp!, {r4, r5, r6, lr}
82: f7ff bffe b.w 0 <spec_wrmem>
82: R_ARM_THM_JUMP24 spec_wrmem
86: 6afb ldr r3, [r7, #44] ; 0x2c
88: 5a18 ldrh r0, [r3, r0]
8a: e7d4 b.n 36 <rtn11+0x36>
8c: 6afb ldr r3, [r7, #44] ; 0x2c
8e: 5519 strb r1, [r3, r4]
90: e7e9 b.n 66 <rtn11+0x66>
92: 6afb ldr r3, [r7, #44] ; 0x2c
94: 5419 strb r1, [r3, r0]
96: bd70 pop {r4, r5, r6, pc}
-
... Na kodu je videt, ze se tam opakuje vlastne 3x vypocet, ktery gcc neumelo eliminovat, nektere vysledky by sly pouzit opakovane, ale pocitaji se porad znovu. Ten kod je opravdu neoptimalni a hnusny, s tim s bude muset neco udelat. Gcc to dela uplne vsude, x86/x64/arm/aarch64/ppc/riscv/xtensa/... ; JENZE na RISC-V ma tento kod vice nez dvojnasobnou delku na instrukce i delku (nebudu to sem vkladat, je to fakt nuda). Musim vyzkouset jestli primeju gcc pres nejake pomocne promenne ty mezivypocty recyklovat.
Jakýkoliv použitelný překladač Cčka ty mezivýpočty nebude recyklovat, protože nesmí! Jsou tam volání na nějaké neznámé spec_* funkce o kterých překladač neví vůbec nic. Takže musí předpokládat, že můžou hrabat úplně na všechno.
Máte tam tři tvrdé resety optimalizátoru a kromě toho ten kód dělá velké kulové. Na tomhle kódu ani neporovnáte optimalizátory ale jen volací konvence různých architektur.
-
Volani tech fci jsou fallbacky kdyz neni pristup povolen a neni to cross-page (jsou tam jinak ldr/str), takze bez nich by to zachovat mohl, ale vidite, mozna -Os zauradoval a to mi nedoslo. Zkousel jsem driv jako prvni pokus to napocitat dopredu pri vypoctu ty hodnoty do nejakych promennych bokem, ale to vedlo k uplne stejnemu vysledku, tak nevim kde je chyba. Az vyresim jine problemy, zkusim si s tim jeste pohrat. Prvni kandidat na zlepseni je treba ten zdvojeny write po bytech dole, tim zmizne vic kodu.
Narazil jsem ale i v x86 kodu na dalsi potiz, ktera se bude resit daleko hure nez u toho ARMu:
3d: 75 09 jne 48
3f: 5b pop %ebx
40: 89 c1 mov %eax,%ecx
42: 5e pop %esi
43: e9 fc ff ff ff jmp 44
44: R_386_PC32 xxxx
48: 5b pop %ebx
49: 5e pop %esi
4a: c3 ret
-
Jak naznacil JH, - tak mi prijde, ze mezivysledek si prekladac neni schopen odlozit.
Normalni programator by treba pouzil "push/pop", coz ale pak rozbije ESP takze vsechny lokalni promenne skrze [esp+offset] budou spatne.
Nebylo by resenim ty opakujici se vypocty ve zdrojaku vyloucit explicitne do promenny napr.?
-
43: e9 fc ff ff ff jmp 44
44: R_386_PC32 xxxx
Neni tohle nejaka dynamicka vazba, ktera bude nahrazena z exports/linkerem?
-
43: e9 fc ff ff ff jmp 44
44: R_386_PC32 xxxx
Tohle je tail call. A pokud se pořádně podíváte, tak je i v předchozím ARMovém kódu.
-
43: e9 fc ff ff ff jmp 44
44: R_386_PC32 xxxx
Tohle je tail call. A pokud se pořádně podíváte, tak je i v předchozím ARMovém kódu.
Ano, ja to vim, ale pro sve ucely (viz zacatek diskuse) ho potrebuju zlikvidovat, aby beh koncil opravdu na nejspodnejsi instrukci. Napadlo mne na konec funkce hodit nejaky asm, ktery by tomu zabranil a pak bych si ho automatizovane odstranoval, ale treba je nejaka lepsi cesta.
-
43: e9 fc ff ff ff jmp 44
44: R_386_PC32 xxxx
Tohle je tail call. A pokud se pořádně podíváte, tak je i v předchozím ARMovém kódu.
Ano, ja to vim, ale pro sve ucely (viz zacatek diskuse) ho potrebuju zlikvidovat, aby beh koncil opravdu na nejspodnejsi instrukci. Napadlo mne na konec funkce hodit nejaky asm, ktery by tomu zabranil a pak bych si ho automatizovane odstranoval, ale treba je nejaka lepsi cesta.
Třeba pro IACA se zajímavý kus kódu značkuje těmahle makry : https://github.com/sifrrich/matrixmult/blob/master/iacaMarks.h
Pokud to má jít do nějakého interního toolu, tak doporučuju pečlivě pročíst dokumentaci k gcc v módu jazykového právníka. On může překladač legálně dělat dost divoké věci. Pokud to musí jít k zákazníkovi, tak stojí za úvahu i commit-push-výpověď ;)
-
"gcc v módu jazykového právníka" - tomu nerozumim o co jde.
Ano, neco takoveho jako ty znacky jsem myslel. Podobne znacky pouzivame na oznaceni vlastniho kodu pro snazsi dokazovani hloupe copy&paste kradeze IP (ale jsou spustitelne a kodove neutralni; v jedne je treba v ASCII moje prijmeni nebo jmeno firmy :) ).
Zkusim to tam nekdy dodratovat, no nebude to zase tak jednoduche, treba to ten tail call nakonec vyresi. Udelam pak seriozni strojovou analyzu tech vystupu, to co jsem tady psal je jen to co bylo videt na prvni pohled.
RDa: mel jsem tam po vypoctu eaddr i prirazeni do lokalni promenne s page_index a page_mask, ktere jsem pak pouzival. Mozna mam botu jeste jinde, ze se tam ta inline fce na vypocet eaddr vola treba nejak 2x, tezko rict, ale nenasel jsem to. Ten kod je pro tento ucel dost hrozny, budu to muset prelozit se zakazanym inlinovanim.
Jeste vlastne resim jeden problem, mam generovany .h soubor, ve kterem je 1 .. n konstant. Aktualne je mam jako
#define CODE_CONST0 0x...
#define CODE_CONST1 0x...
#define CODE_CONST2 0x...
potreboval bych udelat makro GET_NEXT_CODE_CONST() ktere vrati prvni konstantu, ktera jeste nebyla pouzita (takhle si vyzobavam opkody). Dobre mi to funguje na variabilni data, ktera mam jako argumenty pres (register int arg1,register int arg2) v tomto stylu ala vararg:
#define instr_get_nip() (++n_arg, (n_arg==1) ? (arg1) : ((n_arg==2) ? (arg2) : FAILURE))
jenze na ty fixni konstanty se z toho GCC nejak poblazni a prepise mi to do kodu i s tou detekcni promennou FAILURE, prestoze to je jinak uplne stejne jako na ty argumenty. Nechapu to.
-
"gcc v módu jazykového právníka" - tomu nerozumim o co jde.
Dokumentaci interpretovat spíš jako právník než programátor. Nikdy jste neslyšel pojem "language lawyer"?
-
Chapal jsem ten anglicky termin trosku jinak, tak mi to nedavalo smysl k ceskemu. Dekuji za upresneni.
Pridal jsem na konec kodu endmarker, validni instrukci aby to slo objdumpovat, zatim jen pro x86
#define ENDMARKER() __asm__ __volatile__ ("\n\t .byte 0x3f"); // "AAS"
tail cally to vyresilo spolehlive. Co mne ale prekvapilo, nektere snippety jsou diky tomu kratsi, nekdy i o mnoho bajtu :). Samozrejme u spousty tam ta jedna instrukce pribyla, ale je na konci, takze zkusim pripadne napsat neco co ji zlikviduje. Funguje stejne i toto:
#define ENDMARKER() __asm__ __volatile__ ("\n\t"); // jen likvidace tail callu
Co mne nyni trapi v tom, abych to generovani nejak mohl automatizoval je nefunkcnost makra, toto je prototyp pro jeden opkod (realne se pak pouzije misto FAILURE rozvoj do dalsich)
mam definovano:
opcodes.h:#define OPCODE_BYTE_1 0xnn
sem by se to melo dosadit:
opcode = instr_get_opcbyte();
toto makro funguje 100%, gcc optimalizuje podle toho define
cpuhelpers.c:#define instr_get_opcbyte() OPCODE_BYTE_1
pred instr_get_opcbyte(); volam:
cpu.c: n_opcode = 0;
toto makro
cpulelpers.c:#define instr_get_opcbyte() (++n_opcode, (n_opcode == 1) ? OPCODE_BYTE_1 : FAILURE)
vubec nefunguje. Zcela identicky ekvivalent pro arg1/arg2 jak jsem psal vyse mi chodi naprosto bez problemu. tady to makro se rozvine z nejakeho zahadneho duvodu, jako kdyby gcc neumelo uvazovat o fixni hodnote n_opcode a predpocitat to na fixni vysledek OPCODE_BYTE_1 ! Netusite nekdo kde delam chybu ? Prijde mi to uplne silene, hlavne kdyz daleko slozitejsi varianta s registrovymi argumenty chodi jak ma. Prijde mi jako kdybych nevidel nejaky preklep nebo tak neco.
Dekuji za pripadne popostrceni.
-
cpulelpers.c:#define instr_get_opcbyte() (++n_opcode, (n_opcode == 1) ? OPCODE_BYTE_1 : FAILURE)
C makroprocessor jen nahrazuje detekovana makra v textu jejich expanzi, takze pri expanzi makra instr_get_opcbyte() je pro nej vse az k OPCODE_BYTE_1 text, ktery kopiruje, OPCODE_BYTE_1 expanduje na 0xnn, pak zase kopiruje (" : "), a nakonec expanze FAILURE. To az kompilator bere v potaz n_opcode = 0 a optimalizuje kod.
Ale asi presne nerozumim vasemu dotazu a ceho presne chcete dosahnout.
-
Jinak pokud jde o postupne tahani "konstant z pole", co treba pouzit spojeni enum a __COUNTER__:
#include <stdio.h>
enum {
OPC0 = 0,
OPC1 = 10,
OPC2 = 20,
OPC3 = 30,
OPC4 = 40
};
#define CONCAT(a, b) a##b
#define CONCATW(a, b) CONCAT(a, b)
#define GETNEXTOPC CONCATW( OPC, __COUNTER__ )
int main()
{
int opc = GETNEXTOPC;
printf("%d\n", opc);
opc = GETNEXTOPC;
printf("%d\n", opc);
opc = GETNEXTOPC;
printf("%d\n", opc);
opc = GETNEXTOPC;
printf("%d\n", opc);
opc = GETNEXTOPC;
printf("%d\n", opc);
return 0;
}
s vysledkem
0
10
20
30
40
-
Funkci preprocessoru znam, problem jsem uz nakonec lokalizoval. gcc nedokaze ten otaznikovy vyraz vyhodnotit pokud je v cyklu "while (runonlyonce) { ... }", tedy cyklus probehne jen jednou. V jednom miste jsem to tak mel a rozbilo to vsechno okolo. Zvlastni je, ze jina vyhodnoceni v takovem cyklu necini problem, gcc je napocita a bere je jako konstanty. A ve vysledku ten cyklus nikde ve vystupnim kodu neni ... zvlastni.
__COUNTER__ bohuzel neni pro mne v teto ciste podobe reseni, potreboval bych tech counteru vic - problem jsem trosku zjednodusil.
Kazdopadne diky za pomoc.
Ted musim udelat automat, ktery projde ten opkodovy prostor, a pak zkusim trochu optimalizovat vystupni kod, aby to nebyly takove silene bloby plne opakujiciho se kodu.
-
Zkousim ted jestli se podari nejak elegantne pres inline assembler pro x86-64+ARM udelat vyhodnoceni flagu. Jde o to, ze potrebuju udelat nejakou ALU operaci a ulozit si z ni flagy, abych je pak dodatecne nemusel slozite dopocitavat, resp. vytahnul si z flags registru jestli bylo carry a pracoval s nim nasledne.
Muj kod vypada takto:
vypocet ea
load
pripadny druhy dopocet ea
alu
store
dalsi prace s flagy
mezi alu a "dalsi prace s flagy" nemam zaruceno, ze se mi neprepisou. Takze prepisu alu z C operace na inline asm + store flagu do nejakeho registru, ktery nasledne pouziju pri "dalsi prace s flagy".
U ARMu je to primocare - msr SPSR. Jak to resit u x86-64 ? lahf/sahf nemusi byt podporovane, nejlepsi co mne napada je nejake pushf/pop eax, ale mozna existuje lepsi varianta?
Pripadne pokud tu jsou odbornici i na jine ISA, jak se to dela u nich? Ne ze bych potreboval ten translator mit treba na LX6 nebo MIPS, spis jsem se do toho vystupu zakoukal a ucim se na tom nejake nove veci. Zatim se mi muj napad zda celkem jednoduse realizovatelny, tak bych to mozna ze studijnich duvodu vyzkousel taky.