Problem mit Inline Assembler Routine (es wird zu viel optimiert)

Gast #5790689
Lesenswert?

Hallo,

IDE: AtmelStudio 7.0.1931 (Optimierung steht auf -Os)
MCU: ATXMega128A1U mit ext. SDRAM

Ich habe mir ein paar Routinen (Write/Read Byte/Word/DWord/Block, Copy 
Block, ...) in Inline Assembler geschrieben, um auf den externen 
Speicher zugreifen zu können. Eine dieser Funktionen macht allerdings 
Probleme.

Funktion:
1
static inline bool himem_fill_block(uint8_t seg, uint16_t off, uint8_t val, uint16_t len)
2
{
3
  __asm__ __volatile__
4
  (
5
    "in __tmp_reg__,%4"  "\n\t"
6
    "out %4,%1"          "\n\t"
7
    "cp  %A3,r1"         "\n\t"
8
    "cpc %B3,r1"         "\n\t"
9
    "breq END%="         "\n\t"
10
    "LOOP%=:"            "\n\t"
11
    "st %a2+,%0"         "\n\t"
12
    "subi %A3,0x01"      "\n\t"
13
    "sbci %B3,0x00"      "\n\t"
14
    "brne LOOP%="        "\n\t"
15
    "END%=:"             "\n\t"
16
    "out %4,__tmp_reg__" "\n\t"
17
    :
18
    :  "r"  (val),
19
      "r"  (seg),
20
      "z"  (off),
21
      "d"  (len),
22
      "I"  (_SFR_IO_ADDR(RAMPZ))
23
  );
24
  return TRUE;
25
}

Mit dieser Funktion kann ich einen Speicherbereich initialisieren. An 
sich läuft die Funktion. Wenn allerdings der Offset zufällig die gleiche 
Größe wie die Länge hat, optimiert der Compiler etwas zu viel - trotz 
dem volatile.


Beispiel-Listing OK:  (seg=0x80, off=0x0010, val=0x11, len=0x000f)
1
LDI R18,0x0F    Load immediate
2
LDI R19,0x00    Load immediate
3
LDI R30,0x10    Load immediate
4
LDI R31,0x00    Load immediate
5
LDI R25,0x80    Load immediate
6
LDI R24,0x11    Load immediate
7
IN R0,0x3B    In from I/O location
8
OUT 0x3B,R25    Out to I/O location
9
CP R18,R1    Compare
10
CPC R19,R1    Compare with carry
11
BREQ PC+0x05    Branch if equal
12
ST Z+,R24    Store indirect and postincrement
13
SUBI R18,0x01    Subtract immediate
14
SBCI R19,0x00    Subtract immediate with carry
15
BRNE PC-0x03    Branch if not equal
16
OUT 0x3B,R0    Out to I/O location


Beispiel-Listing FAIL:  (seg=0x80, off=0x0010, val=0x11, len=0x0010)
1
LDI R30,0x10    Load immediate
2
LDI R31,0x00    Load immediate
3
LDI R25,0x80    Load immediate
4
LDI R24,0x11    Load immediate
5
IN R0,0x3B    In from I/O location
6
OUT 0x3B,R25    Out to I/O location
7
CP R30,R1    Compare
8
CPC R31,R1    Compare with carry
9
BREQ PC+0x05    Branch if equal
10
ST Z+,R24    Store indirect and postincrement
11
SUBI R30,0x01    Subtract immediate
12
SBCI R31,0x00    Subtract immediate with carry
13
BRNE PC-0x03    Branch if not equal
14
OUT 0x3B,R0    Out to I/O location


Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren? 
;-)

Danke!
Moderator Persönliche Seite #5790725
Lesenswert?

Der T. schrieb:

> IDE: AtmelStudio 7.0.1931 (Optimierung steht auf -Os)

Interessiert nicht. :)  Wenn schon, würde die Compilerversion 
interessieren.

> Speicher zugreifen zu können. Eine dieser Funktionen macht allerdings
> Probleme.

Mit anderen Worten: dein Inline-Assembler-Code ist nicht korrekt 
constrained.

> Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren?

Es ist die Aufgabe des Compilers, Code zu optimieren. Deinen 
Inline-Assembler-Code selbst kann er natürlich dabei nicht anfassen, nur 
die Vor- und Nachbereitung. Die constraints dienen dazu, dass du ihm 
mitteilst, an welchen Stellen er was wie hinterlegen muss.

Soviel zur Vorrede. :-)  Jetzt muss ich aber auch erstmal gucken, was du 
da genau vergessen haben könntest …

Probier doch mal, die Länge als "+d" zu constrainen.

Korrekterweise müsstest du wohl auch den Offset als "+z" deklarieren, 
denn durch das Z+ änderst du ja den Z-Pointer innerhalb des Codes.

Ich bin übrigens nicht wirklich davon überzeugt, dass man das nicht auch 
gleich in C schreiben könnte.
Gast #5790743
Lesenswert?

Jörg W. schrieb:

> Probier doch mal, die Länge als "+d" zu constrainen.
>
> Korrekterweise müsstest du wohl auch den Offset als "+z" deklarieren,
> denn durch das Z+ änderst du ja den Z-Pointer innerhalb des Codes.

Der Compiler mag das "+" nicht..?
1
    :  "r"  (val),
2
      "r"  (seg),
3
      "z"  (off),
4
      "+d"  (len),
5
      "I"  (_SFR_IO_ADDR(RAMPZ))

Dieser meckert dann mit dem Fehler: "input operand constraint contains 
'+'"
Gast #5790767
Lesenswert?

Jörg W. schrieb:
> Dann musst du ihn wohl bei den output operands angeben (also nach dem
> ersten Doppelpunkt).
>
> Allerdings ändert sich dann leider die Nummerierung. Nummerierte
> Operanden sind aber eh bäh, besser sind benannte Operanden.  Also ein
> [foo] for das Constraint, und du kannst statt %0 %[foo] schreiben.


Das habe ich auch gerade ausprobiert - Pustekuchen!
Der Compiler optimiert hier ebenfalls.. :-(

Das Listing sieht fast genauso wie der FAIL oben aus.
Beitrag #5791155 wurde von einem Moderator gelöscht.
Persönliche Seite #5791222
Lesenswert?

Der T. schrieb:

> Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren?

Das ist nicht der richtige Ansatz.  Der richtige Ansatz ist, korrekten 
Code zu schreiben, z.B. so:
1
static inline bool himem_fill_block (uint8_t seg, uint16_t off, uint8_t val, uint16_t len)
2
{
3
    if (len)
4
    {
5
      __asm__ __volatile__
6
      (
7
        "out __RAMPZ__, %[seg]"       "\n"
8
        "LOOP%=:"                     "\n\t"
9
        "st %a[off]+, %[val]"         "\n\t"
10
        "sbiw %[len], 1"              "\n\t"
11
        "brne LOOP%="                 "\n\t"
12
        "out __RAMPZ__, __zero_reg__"
13
        : [off] "+z" (off),
14
          [len] "+w" (len)
15
        : [val] "r" (val),
16
          [seg] "r" (seg)
17
        : "memory");
18
    }
19

20
  return true;
21
}

off und len werden verändert -> Müssen also zu den Outputs wie Jörg 
bereits schrieb.

Speicher wird verändert -> "memory" muss zu den Clobbers.

avr-gcc geht davon aus, dass in RAMP* immer 0 steht -> Der Inhalt von 
RAMPZ braucht nicht gemerkt zu werden, und am Ende kann RAMPZ auf 0 
gesetzt werden -> Kürzerer Code.

Am Anfang des s-Files wird __RAMPZ__ zur I/O-Adresse von RAMPZ definiert 
-> Weniger Operanden.

Die 16-Bit Operationen können in einem 16-Bit Befehl abgehandelt werden 
-> Kürzerer Code.

Der zu wartende asm-Code kann weiter verkürzt werden, indem der 
Vergleich von len aus dem asm herausgezogen wird.  Falls len zur 
Compilezeit bekannt ist, entfällt der Vergleich komplett.

stdbool.h (C99) definiert true und false.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren