-
        

Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 14

Thema: Gcc fasst untere Register nicht an

  1. #1
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.07.2006
    Ort
    Karlsruhe/München
    Alter
    27
    Beiträge
    587

    Gcc fasst untere Register nicht an

    Anzeige

    Hallo,

    ich frage mich schon seit längerem, wieso GCC die unteren Register nie anfässt.(Ich verwende avr-gcc 4.1.2.) Im Wiki finde ich folgendes: R1 – R17, R28, R29 allgemeine Register, die durch einen Funktionsaufruf nicht verändert bzw wieder auf den ursprünglichen Wert restauriert werden. Wenn man jetzt aber globale Variablen hat, legt dieser sie in den SRAM. Wieso diese dann nicht in die unteren Register? Wann (außgenommen Funktionsparameterübergabe) werden diese jemals verwendet. Wie bekomme ich den GCC dazu, diese zu verwenden. Ich verliere ungern unnötig 16 Register.

    Ich hoffe Ihr könnt mir helfen.

  2. #2
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    06.02.2005
    Ort
    Hamburg
    Alter
    31
    Beiträge
    4.255
    Könnte damit zusammenhängen, dass die unteren Register nicht alles können, was die oberen können. Für diverse Operationen müsste man daher die Daten aus diesen Registern in eins der oberen Register kopieren und danach wieder zurück. Wahrscheinlich hat man auf die unteren Register verzichtet, um sich diesen Sonderfall zu sparen.

  3. #3
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Was sich im GCC an der Registerallokierung von 3.x nach 4.x geändert hat, kann ich nicht sagen. jedenfalls hat sich das ABI geändert, d.h. die Register werden in der 4.x so verwendet wie in der 3.x.

    Globale/Statische Variablen werden nie in Register allokiert, es sei denn, man definiert sich globale Registervariablen à la

    Code:
    int register var asm ("r2");
    
    void foo (void)
    {
        var >>= 1;
    }
    Code:
    foo:
    	asr r3	 ;  var
    	ror r2	 ;  var
    	ret	 ;
    Davon ist jedoch tunlichst abzuraten: Nehmen wir an, du verwendest so eine Variable in einer ISR und die IRQ trat in einer LIB-Funktion auf, die so ein Register verwendet. Dann hast du ein Problem...

    Ich glaube nicht, daß du die LIBs komplett neu generieren willst und zwar so, daß GCC nirgends die o.g. regs verwendet (abgesehen davon, das Y den Framepionter hält).

    Und selbst wenn du keine LIBs verwendest oder sie mit -ffixed= generiert bekommst, ist die Verwendung globaler Registervariablen anders als die globaler Variablen. Es gibt einige Nebeneffekte, die dir ruckzuck das Programm um die Ohren hauen.

    GCC verwendet die unteren Register deshalb ungerne, weil die Register nicht alle gleichwertig sind. Es gibt für diese GPRs zB kein LDI und eine konstante in so ein GPR laden geht nur aufwändig über einen sekundären Reload (also über ein oberes GPR).

    Die unteren Register werden verwendet, wenn deine Funktionen komplexer werden und wenn du Funktionen hast, die kein Blatt sind:
    Code:
    long long bar (long long a, long long b)
    {
        return a+b;
    }
    Code:
    bar:
    /* prologue: frame size=0 */
    	push r2
    	push r3
    	push r4
    	push r5
    	push r6
    	push r7
    	push r8
    	push r9
    	push r10
    	push r11
    	push r12
    	push r13
    	push r14
    	push r15
    	push r16
    	push r17
    /* prologue end (size=16) */
    	mov r2,r18	 ;  a, a
    	mov r3,r19	 ;  a, a
    	mov r4,r20	 ;  a, a
    	mov r5,r21	 ;  a, a
    	mov r6,r22	 ;  a, a
    	mov r7,r23	 ;  a, a
    	mov r8,r24	 ;  a, a
    	mov r9,r25	 ;  a, a
    	mov r18,r10	 ;  b, b
    	mov r19,r11	 ;  b, b
    	mov r20,r12	 ;  b, b
    	mov r21,r13	 ;  b, b
    	mov r22,r14	 ;  b, b
    	mov r23,r15	 ;  b, b
    	mov r24,r16	 ;  b, b
    	mov r25,r17	 ;  b, b
    	mov r10,r2	 ; ,
    	add r10,r18	 ; , b
    	ldi r30,lo8(1)	 ;  tmp46,
    	cp r10,r2	 ;  tmp45, a
    	brlo .L3	 ; ,
    	ldi r30,lo8(0)	 ;  tmp46,
    .L3:
    	mov r11,r3	 ; ,
    	add r11,r19	 ; , b
    	ldi r26,lo8(1)	 ;  tmp47,
    	cp r11,r3	 ; , a
    	brlo .L4	 ; ,
    	ldi r26,lo8(0)	 ;  tmp47,
    .L4:
    	add r30,r11	 ;  tmp48,
    	ldi r31,lo8(1)	 ;  tmp49,
    	cp r30,r11	 ;  tmp48,
    	brlo .L5	 ; ,
    	ldi r31,lo8(0)	 ;  tmp49,
    .L5:
    	or r26,r31	 ;  tmp47, tmp49
    	mov r11,r30	 ; , tmp48
    	mov r12,r4	 ; ,
    	add r12,r20	 ; , b
    	ldi r27,lo8(1)	 ;  tmp50,
    	cp r12,r4	 ; , a
    	brlo .L6	 ; ,
    	ldi r27,lo8(0)	 ;  tmp50,
    .L6:
    	mov r30,r26	 ;  tmp51, tmp47
    	add r30,r12	 ;  tmp51,
    	ldi r31,lo8(1)	 ;  tmp52,
    	cp r30,r12	 ;  tmp51,
    	brlo .L7	 ; ,
    	ldi r31,lo8(0)	 ;  tmp52,
    .L7:
    	or r27,r31	 ;  tmp50, tmp52
    	mov r12,r30	 ; , tmp51
    	mov r13,r5	 ; ,
    	add r13,r21	 ; , b
    	ldi r26,lo8(1)	 ;  tmp53,
    	cp r13,r5	 ; , a
    	brlo .L8	 ; ,
    	ldi r26,lo8(0)	 ;  tmp53,
    .L8:
    	mov r30,r27	 ;  tmp54, tmp50
    	add r30,r13	 ;  tmp54,
    	ldi r31,lo8(1)	 ;  tmp55,
    	cp r30,r13	 ;  tmp54,
    	brlo .L9	 ; ,
    	ldi r31,lo8(0)	 ;  tmp55,
    .L9:
    	or r26,r31	 ;  tmp53, tmp55
    	mov r13,r30	 ; , tmp54
    	mov r14,r6	 ; ,
    	add r14,r22	 ; , b
    	ldi r27,lo8(1)	 ;  tmp56,
    	cp r14,r6	 ; , a
    	brlo .L10	 ; ,
    	ldi r27,lo8(0)	 ;  tmp56,
    .L10:
    	mov r30,r26	 ;  tmp57, tmp53
    	add r30,r14	 ;  tmp57,
    	ldi r31,lo8(1)	 ;  tmp58,
    	cp r30,r14	 ;  tmp57,
    	brlo .L11	 ; ,
    	ldi r31,lo8(0)	 ;  tmp58,
    .L11:
    	or r27,r31	 ;  tmp56, tmp58
    	mov r14,r30	 ; , tmp57
    	mov r15,r7	 ; ,
    	add r15,r23	 ; , b
    	ldi r26,lo8(1)	 ;  tmp59,
    	cp r15,r7	 ; , a
    	brlo .L12	 ; ,
    	ldi r26,lo8(0)	 ;  tmp59,
    .L12:
    	mov r30,r27	 ;  tmp60, tmp56
    	add r30,r15	 ;  tmp60,
    	ldi r31,lo8(1)	 ;  tmp61,
    	cp r30,r15	 ;  tmp60,
    	brlo .L13	 ; ,
    	ldi r31,lo8(0)	 ;  tmp61,
    .L13:
    	or r26,r31	 ;  tmp59, tmp61
    	mov r15,r30	 ; , tmp60
    	mov r16,r8	 ; ,
    	add r16,r24	 ; , b
    	ldi r27,lo8(1)	 ;  tmp62,
    	cp r16,r8	 ; , a
    	brlo .L14	 ; ,
    	ldi r27,lo8(0)	 ;  tmp62,
    .L14:
    	mov r31,r26	 ;  tmp63, tmp59
    	add r31,r16	 ;  tmp63,
    	ldi r30,lo8(1)	 ;  tmp64,
    	cp r31,r16	 ;  tmp63,
    	brlo .L15	 ; ,
    	ldi r30,lo8(0)	 ;  tmp64,
    .L15:
    	or r30,r27	 ;  tmp65, tmp62
    	mov r17,r9	 ; ,
    	add r17,r25	 ; , b
    	add r30,r17	 ;  tmp65,
    	mov r18,r10	 ;  <result>, <result>
    	mov r19,r11	 ; , <result>
    	mov r20,r12	 ; , <result>
    	mov r21,r13	 ; , <result>
    	mov r22,r14	 ; , <result>
    	mov r23,r15	 ; , <result>
    	mov r24,r31	 ; , <result>
    	mov r25,r30	 ; , tmp65
    /* epilogue: frame size=0 */
    	pop r17
    	pop r16
    	pop r15
    	pop r14
    	pop r13
    	pop r12
    	pop r11
    	pop r10
    	pop r9
    	pop r8
    	pop r7
    	pop r6
    	pop r5
    	pop r4
    	pop r3
    	pop r2
    	ret
    Anm: Der Code ist trotz -Os nicht optimal, weil wenig AUfwand in 64-Bit-Typen gesteckt wurde und GCC die Addition algebraisch austextet.

    Das Beispiel zeigt aber, daß GCC durchaus diese Register verwendet.

    ZU R1: GCC geht davon aus, daß da die 0 drin steht.
    Disclaimer: none. Sue me.

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    typo: das ABI hat sich natürlich *nicht* geändert!
    Disclaimer: none. Sue me.

  5. #5
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.07.2006
    Ort
    Karlsruhe/München
    Alter
    27
    Beiträge
    587
    Vielen Dank Sprinter für deine ausfürliche Antwort.

    Ich habe noch eine Frage: Da der GCC4 die unteren Register R2-R17 ja nicht zum Variablenspeichern verwendet, wieso Pushed und Popped(nicht falsch verstehen ) er das dann? Das ist doch Zeit und Resourcenverschwendung?

    Grüße

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Ich glaub, da herrscht etwas Verwirrung...

    Variablen (d.gh allgemeiner Objekte im Sinne von C) haben einen Speicherort. Globale und statische Variablen leben zB üblicherweise im SRAM (auch möglich sind Flash und EEPROM).

    Zur Verarbeitung muss ein Compiler einen Werte von dem Ort, wo er "lebt", in ein Register (GPR) laden, um die gewünschten Operationen damit anzustellen. Das ist notwendig, weil kaum eine Architektur Verarbeitung direkt im RAM erlaubt. AVR zB erlaubt nur Lesen/Schreiben von Werten und das Testen von Bits im bit-adressierbaren SFR-Bereich.

    Wird nun eine solche Variable gebraucht, dann muss sie also geladen werden, zB nach R25:R24 für eine 16-Bit-Variable.

    Wird nach dem Laden eine Funktion ausgeführt, dann muss avr-gcc den Wert in ein Register sichern, das nicht von der Funktion verändert wird, zB R17:R16. Braucht die aufgerufene Funktion ihrerseits zB R17, muss es gesichert werden, weil der Aufrufer davon ausgeht.

    Diese Variablen werden nicht in GPRs angelegt (leben also nicht dort), werden aber evtl durchaus dort hinkopiert.

    Lokale Variablen (genauer: automatische Variablen) werden von GCC in GPRs oder im Frame der Funktion angelegt, falls sie nicht wegoptimiert werden.

    Code:
    long foo (long i)
    {
        if (i)
            return foo (i)+i;
            
        return i;
    }
    Code:
    foo:
    /* prologue: frame size=0 */
    	push r14
    	push r15
    	push r16
    	push r17
    /* prologue end (size=4) */
    	movw r14,r22	 ;  i, i
    	movw r16,r24	 ;  i, i
    	cp r22,__zero_reg__	 ;  i
    	cpc r23,__zero_reg__	 ;  i
    	cpc r24,__zero_reg__	 ;  i
    	cpc r25,__zero_reg__	 ;  i
    	breq .L2	 ; ,
    	rcall foo	 ; 
    	add r22,r14	 ;  <result>, i
    	adc r23,r15	 ;  <result>, i
    	adc r24,r16	 ;  <result>, i
    	adc r25,r17	 ;  <result>, i
    	rjmp .L1	 ; 
    .L2:
    .L1:
    /* epilogue: frame size=0 */
    	pop r17
    	pop r16
    	pop r15
    	pop r14
    	ret
    Mal abgesehen davon, daß das Beispielprogramm recht sinnlos ist: GCC macht hier guten Code und das Sichern der Register (i in R22-R25 nach R14-R17) um den Aufruf von foo ist nicht überflüssig.

    Hast du ein konkretes, überschaubares Beispiel da, von dem du denkst, daß GCC schlecht arbeitet? Vielleicht kann man daran diskutieren, was abgeht.

    Übersetzen am besten mit -O2 oder -Os zusammen mit -S und -fverbose-asm.
    Disclaimer: none. Sue me.

  7. #7
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.07.2006
    Ort
    Karlsruhe/München
    Alter
    27
    Beiträge
    587
    Hallo Sprinter.

    Danke für die ausfürliche Antwort.

    Zu deinem Bsp.
    Wieso muss GCC diese sichern? Das ganze geht ja auch ohne das sichern. Es geht doch auch add R22,R22 ?

    Ich habe mal ein Beispiel, bei dem der GCC (für mich) unverständlich viel Pushed.
    Code:
    long foo(long a, long b, long c, uint8_t d){
      if(d){
        return a+b;
      }else{
        return a-c;
      }
    }
    Nun das Listing:
    Code:
    long foo(long a, long b, long c, uint8_t d){
      4e:	cf 92       	push	r12 ;Alle unteren Register werden gepushed
      50:	ef 92       	push	r14 ;obwohl sie nicht verändert werden
      52:	ff 92       	push	r15 ;
      54:	0f 93       	push	r16
      56:	1f 93       	push	r17
      if(d){
      58:	cc 20       	and	r12, r12
      5a:	29 f0       	breq	.+10     	; 0x66 <foo+0x18>
        return a+b;
      5c:	62 0f       	add	r22, r18
      5e:	73 1f       	adc	r23, r19
      60:	84 1f       	adc	r24, r20
      62:	95 1f       	adc	r25, r21
      64:	04 c0       	rjmp	.+8      	; 0x6e <foo+0x20>
      }else{
        return a-c;
      66:	6e 19       	sub	r22, r14
      68:	7f 09       	sbc	r23, r15
      6a:	80 0b       	sbc	r24, r16
      6c:	91 0b       	sbc	r25, r17
      6e:	1f 91       	pop	r17 ;und wieder restaurieren, obwohl sie 
      70:	0f 91       	pop	r16 ;unberührt sind...
      72:	ff 90       	pop	r15
      74:	ef 90       	pop	r14
      76:	cf 90       	pop	r12
      78:	08 95       	ret
    GCC ist jedoch, obwohl er diese Eigenheit hat. einer der besten Compiler. Recursive funktionen macht er z.T. auch ohne Pushen. Sofern halt wieder die unteren Register nicht berührt werden.

    Mir ist bewust, dass GCC die unteren Register nur zum Teil verwenden kann. Das ist doch aber kein Grund, diese zu Pushen, obwohl die nicht verändert werden. Kann man das dem GCC irgendwie abgewöhnen?

    Grüße

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Ja, hier werden die Register unnötigerweise gesichert, weil sie nicht verändert werden.

    IMHO ist das eine Unzulänglichkeit in GCC (also nicht nur in avr-gcc).

    Ich kann die erläutern, wo das Problem herkommt, und wo du es fixen kannst:

    Du übersetzt mit -da -dp -save-temps. Das erzeugt dir zig RTL (register transfer language) dumps.
    Einer der komplexesten Teile von GCC (und vieler anderer optimierender Compiler) ist die Register-Allokierung. In diesen Passes werden die unendlich vielen Pseudo-Register, in denen die Werte gehalten werden, auf die endlich vielen Hard-Register der Maschine abgebildet. Falls die Hard-Register nicht ausreichen, landen die Werte in Stack-Slots und müssen zur Verabreitung vom Stack geladen werden, wofür dann wieder andere Hard-Register frei geschaufelt werden müssen, etc.

    In Pass lreg (local register allocation = reg-alloc innerhalb von basic blocks) ist noch alles ok, zB
    Code:
    (insn 5 4 6 0 (set (reg/v:SI 44 [ c ])
            (reg:SI 14 r14 [ c ])) 14 {*movsi} (nil)
        (expr_list:REG_DEAD (reg:SI 14 r14 [ c ])
            (nil)))
    (reg 44) ist ein SI-Pseudo (also 32-Bit) und wird geladen vom SI-Hard-Reg 14, einem incoming arg (=c) von foo. In dieser insn stirbt R14 (also R14...R17), weil es jetzt ja in R44 steht. Ich vereinfache mal das insn-Pattern auf das Wesentliche:

    Code:
    (set (reg:SI 44)
         (reg:SI 14))
    In Pass greg+postreload (global register allocation) wird daraus
    Code:
    (set (reg:SI 14)
         (reg:SI 14))
    d.h. GCC allokiert R44 nach R14 (eine gute Wahl) und in flow2 wird erkannt, daß insn 5 trivial ist und weggeworfen. Allerdings lebt R14 weiterhin, weil es ja verwendet wird (wenn auch nirgends explizit gesetzt wird).

    In ./gcc/config/avr/avr.c:function_prologue() und :function_epilogue() wird der asm-Code für Funktions-Prolog und -Epilog ausgegeben, der sich u.a. darum kümmert, Regs die leben *und* call save sind (als nach einer Funktion unverändert vorliegen) zu sichern, d.h. dort werden die überflüssigen push/pop emittiert.

    Die Info, ob ein (pseudo oder hard) Reg lebt, wird in regs_ever_live[] gemerkt. Und dort steht eben nur, ob ein Reg lebt, aber nicht, ob es verändert wird oder nicht.

    Um den Code zu verbessern, müsste man also an dieser Stelle nicht auf regs_ever_live[] testen, sondern eine neue live analysis durchführen und testen, ob für diese Register REG_N_SETS() gleich Null ist und einen neuen avr-gcc generieren (bzw ein neuer cc1, das reicht schon). Möglicherweise sieht GCC bzw. die zu REG_N_SETS() betragenden Funktionen in ./gcc/regclass.c, daß R14 zu den virtual-incoming-args Registern gehört, und zählen es zu den gesetzten Registern hinzu.

    Falls du an GCC nicht selbst Hand anlegen willst, kannst du bei http://gcc.gnu.org eine feature-request machen und bei der GCC 4.3 hast du schon besseren Code. (AFAIK ist momentan jedoch Konsolidierung und ein feature freeze).
    Disclaimer: none. Sue me.

  9. #9
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    08.07.2006
    Ort
    Karlsruhe/München
    Alter
    27
    Beiträge
    587
    Hallo Sprinter.

    Ich traue es mir nicht zu, im GCC zumzufummeln. Da habe ich zu wenig Erfahrung dazu. Ich mag mich zwar etwas auszennen, aber das ist doch dann zu kompliziert.

    Ich werde wohl bei GCC einen features-request einreichen, und hoffen das die Verstehen, was ich meine.^^

    Vielen Dank für deine Hilfe.

  10. #10
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Falls es nur wenige, zeitkritische Funktionen sind, um die es geht, bleiben noch folgende Möglichkeiten, ohne GCC anzufassen:

    -- Funktion(en) unter Beachtung des avr-ABI in Assembler / Inline Assembler formulieren. Ob ne Stub zusammen mit Inline Asm hilft, müsste man testen (da besteht uU das Problem weiterhin).
    -- Funktion(en) inlinen, zb mit "inline" oder "__attribute__((always_inline))"
    Disclaimer: none. Sue me.

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •