Jak se C překládá

Z MAM wiki

Přejít na: navigace, hledání

Překladač jazyka C může rovnou vytvářet binární strojový kód. Často je ale možné a vhodné provést nejprve překlad z C do assembleru a následně assembler přeložit do strojového kódu. Při tomto uspořádání může člověk snadno kontrolovat kvalitu překladu. Překladač GCC používá výhradně toto uspořádání, i když meziprodukt v assembleru obvykle neuloží. Můžeme jej ale požádat o překlad jen do assembleru volbou -S. Studium takového překladu krátkých fragmentů kódu je výborným způsobem, jak se zároveň seznámit s assemblerem, s jazykem C i s konvencemi volání procedur. Tato možnost je neocenitelná při optimalizaci programů - můžeme posoudit, zda bychom uměli časově kritickou proceduru napsat v assembleru lépe, než ji přakladač přeložil. Pokud se rozhodneme proceduru přepsat, máme již připravenu kostru se správným předáváním parametrů.

Tento krátký fragment kódu v C sečte pět čísel, uložených od zadané adresy:

char test1( char *x ){
  char i;
  char sum = 0;
  for( i = 0; i < 5; i++ ){
    sum += x[ i ];
  }
  return sum;
}

Do assembleru jej přeložíme příkazem:

avr-gcc -O2 -S test1.c

a dostaneme výsledek:

	mov r22,r24
	mov r23,r25
	ldi r20,lo8(0)
	ldi r18,lo8(0)
	ldi r19,hi8(0)
.L2:
	mov r30,r22
	mov r31,r23
	add r30,r18
	adc r31,r19
	ld r24,Z
	add r20,r24
	subi r18,lo8(-(1))
	sbci r19,hi8(-(1))
	cpi r18,5
	cpc r19,__zero_reg__
	brne .L2
	mov r24,r20
	ret

Zpětný překlad podobného fragmentu do C je jedním z příkladů u zkoušky. Je samozřejmě třeba alespoň přibližně znát instrukční soubor AVR (u zkoušky je k dispozici tabulka instrukcí). V tomto fragmentu je několik drobností, které mohou někoho zaskočit:

  • registr Z - to je jen alias pro dvojici r31, r30
  • funkce lo8() a hi(8) - vyberou spodní či horní byte z konstanty. Překladač je dle logiky věci systematicky použije, i když nám to může připadat zbytečné.
  • __zero_reg__ - to je symbolické jméno pro registr, ve kterém překladač udržuje nulu
  • hodnota r24 a r25 na začátku - tam je dle konvence vstupní šestnáctibitový parametr (adresa pole x)
  • hodnota r24 na konci - tam je dle konvence osmibitový výsledek procedury
  • dvojice subi+sbci - provede šestnáctibitové přičtení konstanty s přenosem přes carry. Je použita konstanta s opačným znaménkem a instrukce pro odčítání, protože vhodné instrukce pro sčítání nejsou.
  • dvojice cpi+cpc - obdobně provede šestnáctibitové porovnání
  • alokace proměnných - sum je v r20, i ve dvojici r18,r19, adresa pole x je přesunuta do r22,r23

Způsob překladu výrazně závisí na zapnutých optimalizacích. Pokud místo -O2 použijeme -O3, překladač nejenže optimalizuje zbytečné přesuny mezi registry, ale v tomto případě dokonce rozvine celou smyčku (nahradí ji pěti příkazy sčítání):

	mov r30,r24
	mov r31,r25
	ld r24,Z
	ldd r25,Z+1
	add r24,r25
	ldd r25,Z+2
	add r24,r25
	ldd r25,Z+3
	add r24,r25
	ldd r25,Z+4
	add r24,r25
	ret

Zde jsou další příklady kódu v C a překladu do assembleu AVR (kompilováno s -O3):

Maximum ze dvou čísel:

char test2( char a, char b ){
  return a>b ? a : b;
}
	cp r22,r24
	brge .L9
	mov r22,r24
.L9:
	mov r24,r22
	ret
Osobní nástroje