ARM GCC Optimierung



  • Hallo, ich versuche mich etwas in den Cortex M0 einzuarbeiten. Dabei bin ich auf folgendes Problem gestoßen:

    void __attribute__((optimize("O0"))) blub(int* t1, int* t2) {
    	__asm(
    			"ldr r5, [%[t2]]\n"
    			"ldr r4, [%[t1]]\n"
    			"add r5, r4, r5\n"
    			"str r5, [%[t1]]\n"
    			:: [t1]"r"(t1), [t2]"r"(t2)
    			);
    }
    
    int main() {
    	dev_io::init();
    
    	int a = 1;
    	int b = 2;
    	blub(&a, &b);
    	if(a == 3) {
    		dev_io::set_ledR();
    	}
    	dev_io::set_ledG();
    	for(;;) {
    	}
    	return 0;
    }
    

    Ich programmiere in C++ mit inline Assembler. Bei eingeschalteter Codeoptimierung optimiert mir der Compiler den blub-Funktionsaufruf, die Konstantenzuweisung auf a und b, sowie die if-Bedingung UND das setzen von ledR einfach raus ...
    Ich muss entweder die Codeoptimierung komplett abschalten oder die Funktion Blub das oben stehende Attribut spendieren, sonst leuchtet meine rote LED nicht ...

    Weiß jemand was da schief läuft?



  • Versuch es mal mit asm volatile statt nur asm. Ansonsten auf inline Assembler verzichten.



  • Volatile bringt leider auch nichts.

    Ansonsten auf inline Assembler verzichten.

    Nur sehr ungern!



  • GCC-Inline-Assembly ist ein Horror für sich. Ich vermute hier mal ins Blaue hinein:

    Nach dem ersten Doppelpunkt kommen die Output-Operanden, nach dem zweiten Doppelpunkt die Input-Operanden, nach dem dritten Doppelpunkt die Clobber-Liste. Soweit ich sehe, gibt es keine Output-Operanden und keine Clobber-Liste. GCC denkt sich also, dass dieser Code-Teil gar nichts verändert und kürzt ihn weg. Es könnte sein, dass schon der Eintrag von R4 und R5 in die Clobber-Liste das Problem löst.

    Ein bisschen erstaunt bin ich, dass die Angabe der Register ohne Prozentzeichen bzw. zweifaches Prozentzeichen klappt.

    Der if-Block wird weggekürzt, da GCC nicht erkennen kann, dass Du die Variable (!) 'a' trickreich per Registerzugriff in der Funktion 'blub' setzen willst. GCC denkt, der Wert von 'a' ist immer noch 1 und die if-Bedingung niemals erfüllt. Probier mal der Definition von a ein volatile voranzustellen: 'volatile int a = 1;'.

    viele grüße
    ralph



  • Wenn ich die Variablen volatile definiere klappt alles wunderbar. Ich hatte aber gehofft das Funktionen die inline-Assembler enthalten ganz normal benutzt werden können 😕



  • Wie schon gesagt wurde, der Compiler weiß nicht das *t1 vom Assembler Code geändert wird (ist Ausgabewert), als auch das r4 und r5 modifiziert werden (das kann zu falschem Code führen, da GCC glaubt der Inhalt dieser Register sei nicht Flüchtig).
    https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html



  • Assembler behindert die Codeoptimierung, die für die Hochsprache gedacht ist.

    Deswegen wäre die bessere Reihenfolge eher so:
    erst Hochsprache
    dann "optimieren"
    dann wenn (überhaupt noch nötig) von Hand nachbessern.

    Oder eben Codeabschnitte wie oben gleich komplett in Assembler schreiben (und auf "optimize" verzichten).



  • Die Constraints sind in jedem Falle falsch.
    Weil der Code keinen explizit deklarierten Output hat, benötigen wir ausserdem volatile.

    void blub(int* t1, int* t2) {
        __asm volatile (
                "ldr r5, [%[t2]]\n"
                "ldr r4, [%[t1]]\n"
                "add r5, r4, r5\n"
                "str r5, [%[t1]]\n"
                :: [t1]"r"(t1), [t2]"r"(t2)
                : "r4", "r5", "memory", "cc"
                );
    }
    

    Besser ist die Verwendung von Speicheroperanden, dann kann auch der Output vernünftig deklariert werden:

    void blub(int* t1, int* t2) {
        __asm(
                "ldr r5, %[t2]\n"
                "ldr r4, %[t1]\n"
                "add r5, r4, r5\n"
                "str r5, %[t1]\n"
                : [t1]"=m"(*t1)
                : "0"(*t1), [t2]"m"(*t2)
                : "r4", "r5", "cc"
                );
    }
    


  • Gezeigter Code ändert keine Flags, daher ist das "cc" unnötig.