Forum: Compiler & IDEs Generierten Assemblercode nach C Präprozessor + Compiler


von Fabian B. (fabs)


Lesenswert?

Hallo,
 zum Überprüfen von meinem Programm würde ich gern mal den vom 
C-Compiler generierten ASM-Code anschauen, und zwar nachdem der 
Präprozessor die Defines berechnet und ersetzt hat, die Kommentare raus 
gestrippt wurden usw.
Das .lss File ist ja ein Anfang, aber da sind die Kommentare alle noch 
drin, die Defines noch nicht ersetzt.

Gibt es eine Möglichkeit ein List-File zu erhalten, das zumindest die 
Defines alle schon "in endgültiger Schreibweise" ersetzt drin hat? So 
könnte man mal überprüfen ob alles so evaluiert, wie man sich das 
erhofft.

Ich verwende AVR-Studio 4.18sp1 und AVR-GCC 20090313

Zur Not kann ich auch mit der Kommandozeile umgehen, aber direkt aus 
AVR-Studio wäre natürlich bequemer...

Gruß
Fabian

von Stefan E. (sternst)


Lesenswert?

Füge den Compiler-Optionen ein -save-temps hinzu. Dann hast du im 
Build-Ordner ("default" falls du keinen eigenen Namen angegeben hast) 
nach dem Build zu jeder C-Datei eine i-Datei und eine s-Datei. Ersteres 
ist der Präprozessor-Output, letzteres der vom Compiler generierte 
ASM-Code.

von Fabian B. (fabs)


Lesenswert?

Hey, Danke. Da hab ich erstmal was zum forschen.

Wird das .lss-File eigentlich nach oder vor dem .i und .s erstellt? Im 
.i schaut's nämlich gut aus, im .lss hingegen ist der Code an manchen 
Stellen wie vermischt, zumindest wenn der darstellte ASM-Code was mit 
den drüber stehenden C-Zeile zu tun hat. Da ist die Abschlusschleife (}) 
eines If schonmal erst nach dem nächsten If-Block, obwohl die sonst 
getrennt und nacheinander stehen sollten.

Gruß
Fabian

von Stefan E. (sternst)


Lesenswert?

Fabian B. schrieb:

> Wird das .lss-File eigentlich nach oder vor dem .i und .s erstellt?

Das lss-File wird erstellt, wenn der eigentliche Build-Prozess komplett 
abgeschlossen ist. Das lss-File ist disassemblierter Objektcode, in den 
mit Hilfe der Debug-Infos der ursprüngliche C-Code "eingemischt" wird. 
Da durch das Optimieren aber häufig kein eindeutiger 1:1 Zusammenhang 
mehr zwischen C und ASM-Code besteht, klapp das oft nicht besonders gut.

von Fabian B. (fabs)


Lesenswert?

Demnach darf man sich also auf die Darstellung (Zuordnung C zu ASM) dort 
nicht verlassen? Das Programm "kann" also funktionieren, obwohl der Code 
mit Zuordnung im .lss nicht unbedingt Sinn macht?

Noch eine Frage zum Präprozessor hätte ich noch:
 gibt es irgendwo eine "einfache" Möglichkeit zu sehen, zu welchem Wert 
Defines evaluieren? Sowas wie eine Übersicht Define->Wert?

Gruß
Fabian

von Fabian B. (fabs)


Lesenswert?

Mist, jetzt ist schon zu spät zum editieren des letzten Posts.

Mathematische (bzw. logische) Ausdrücke aus Konstanten werden ja vom 
Compiler, nicht vom Präprozessor berechnet.

Ich habe den Ausdruck
ID = ((U16)(NID_CONFIG << 7) & (U16)EXT_BIT_MSK) & (U16)ownID;

ID ist eine U16, NID_CONFIG ist ein Define mit dem Wert 0x07 und 
EXT_BIT_MSK ist ein Define mit dem Wert 0x40. ownID ist eine U8 
Variable.
Wird der vordere Teil des Ausdrucks zur Kompilezeit schon berechnet und 
als Konstante im Code abgelegt?

Im .lss-File ist das leider nicht annähernd mehr zu erkennen, im .i ist 
es noch nicht evaluiert und im .s ist das nicht zu finden...

Gibt es noch ein temp-File vom Compiler, wo solche Ausdrücke schon 
verarbeitet sind, der Code aber noch erhalten?

Gruß
Fabian

von Rolf Magnus (Gast)


Lesenswert?

> Demnach darf man sich also auf die Darstellung (Zuordnung C zu ASM) dort
> nicht verlassen? Das Programm "kann" also funktionieren, obwohl der Code
> mit Zuordnung im .lss nicht unbedingt Sinn macht?

Richtig, sofern Optimierungen an sind. Nach ISO-C ist es so, daß der 
Compiler beliebige Änderungen machen, sofern das "beobachtbare 
Verhalten" das gleiche ist. Das beobachtbare Verhalten ist dabei 
definiert durch I/O und den Zugriff auf volatile-Variablen. Diese 
Vorgabe macht sich GCC zunutze und sortiert Teile des Code um, 
kombiniert welche oder entfernt sie ganz, sofern das am Ergebnis nichts 
ändert.

>  gibt es irgendwo eine "einfache" Möglichkeit zu sehen, zu welchem Wert
> Defines evaluieren? Sowas wie eine Übersicht Define->Wert?

Meinst du da nur eine einfach Liste aller Makros?
Das ginge etwa so:
1
cpp -dM datei.c

Oder willst du deinen Quellcode sehen, so wie er nach dem Präprozessor 
aussieht? Dann kannst du ihn einfach durch den Prärozessor laufen 
lassen:
1
cpp datei.c

Mit -dD bekommst du übrigens beides. Wenn du eine schon vorhandene 
gcc-Kommandozeile nutzen willst, kannst du statt cpp einfach gcc -E 
machen und die obigen Optionen anhängen. Nicht vergessen, evtl. ein -o 
foo.o zu entfernen, sonst schreibt er die Ausgabe da rein.

> Mathematische (bzw. logische) Ausdrücke aus Konstanten werden ja vom
> Compiler, nicht vom Präprozessor berechnet.

Größtenteils, ja.

Ich habe den Ausdruck
ID = ((U16)(NID_CONFIG << 7) & (U16)EXT_BIT_MSK) & (U16)ownID;

> Wird der vordere Teil des Ausdrucks zur Kompilezeit schon berechnet und
> als Konstante im Code abgelegt?

Mit eingeschalteten Optimierungen normalerweise ja, aber nicht vom 
Präprozessor.

> Im .lss-File ist das leider nicht annähernd mehr zu erkennen, im .i ist
> es noch nicht evaluiert und im .s ist das nicht zu finden...

Vermutlich wegoptimiert. Möglicherweise macht er die Berechnung erst 
irgendwann später, wenn ID dann benutzt wird. Wenn ID nachher nicht 
benutzt wird, kann's auch sein, daß die Berechnung komplett entfernt 
wird.

> Gibt es noch ein temp-File vom Compiler, wo solche Ausdrücke schon
> verarbeitet sind, der Code aber noch erhalten?

Kann schon sein, aber dann vermutlich nicht als C- oder Assembler-Code, 
sondern als Syntaxbaum.

von Fabian B. (fabs)


Lesenswert?

Ja, Optimierung steht auf -Os.
Ich hatte nur gehofft, dass es noch sowas wie das .s-File gibt, nur halt 
mit diesen Ausdrücken aufgelöst, soweit wie der Compiler das halt macht.

Wegoptimieren dürfte er diese Zeile eigentlich nicht, weil dann die 
Anweisungen danach unsinnig würden.

Aber im .lss-File ist ja eh nix aufgelöstes, sondern da stehen ja wieder 
die Defines mit ihren Symbolen.

Gruß
Fabian

von Peter (Gast)


Lesenswert?

zeig uns doch man den asm code, evnetuell sehe ja andere wie die 
berechnung gemacht wird

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fabian B. schrieb:
> Ja, Optimierung steht auf -Os.
> Ich hatte nur gehofft, dass es noch sowas wie das .s-File gibt, nur halt
> mit diesen Ausdrücken aufgelöst, soweit wie der Compiler das halt macht.

Gibt es auch, aber glaub' mir, das willst du nicht unbedingt
verstehen. ;-)  Du kannst ja spaßeshalber mal die Option -da
ausprobieren...

von Peter D. (peda)


Lesenswert?

Fabian B. schrieb:
> Aber im .lss-File ist ja eh nix aufgelöstes, sondern da stehen ja wieder
> die Defines mit ihren Symbolen.

Es steht darin der C-Code und darunter der generierte Assembler.
Und der Assembler enthält ja dann die Werte als Hex (Kommentar Dezimal) 
in den LDI, CPI usw. Befehlen.


Peter

von Fabian B. (fabs)


Lesenswert?

Okay, vielleicht häng ich gedanklich auch einfach etwas, aber Assembler 
ist mir an sich schon geläufig.
Etwas abweichend von oben:

Struct Variable tx_temp mit member .cmd als ein ENUM von vier möglichen 
Werten, .id.std vom Typ U16 und .dlc vom Typ U8 (und noch ein paar 
mehr). Im .lss steht nun beim Befüllen der Struct:
1
            tx_temp.cmd = CMD_TX;
2
    1928:  9a 83         std  Y+2, r25  ; 0x02
3
                          // ID und Mask setting für BusDiscoveryReply
4
            tx_temp.id.std = ((U16)(NID_CONFIG << 7) & (U16)EXT_BIT_MSK) & (U16)ownID;  // Nur Ext-Bit gesetzt
5
    192a:  1c 82         std  Y+4, r1  ; 0x04
6
    192c:  1b 82         std  Y+3, r1  ; 0x03
7
8
            tx_temp.dlc = 1;      // Länge der Nachricht = 1
9
    192e:  9b 87         std  Y+11, r25  ; 0x0b

NID_CONFIG ist in diesem Fall 0x00 (define), EXT_BIT_MSK ist 0x40 
(define), ownID ist eine U8 Variable.
Was mich nun wundert: das Y Register ist ja R28,R29. Dieses wird 
nirgends in der Funktion vor diesen Befehlen beschrieben. Bei den 
Befehlen davor kann ich die ASM-Anweisungen durchaus nachvollziehen.

Und bei der Zeile mit den Defines müsste von dem Ausdruck ja eigentlich 
sowas wie (U16)(0x40 & ownID) übrig bleiben. Seh nur ich das in den zwei 
Assembler-Anweisungen nicht (Zeile 192a, 192c)?

Vielleicht kann mich da jemand Erhellen. ;-)

Gruß
Fabian

von Peter D. (peda)


Lesenswert?

Fabian B. schrieb:
> NID_CONFIG ist in diesem Fall 0x00 (define)

Dann ist jede Verundung damit auch 0 und damit ist Laden mit R1 
(Nullregister) korrekt.

> Was mich nun wundert: das Y Register ist ja R28,R29. Dieses wird
> nirgends in der Funktion vor diesen Befehlen beschrieben.

Vielleicht vom Aufrufer.


Peter

von Oliver (Gast)


Lesenswert?

Wenn du alle defines loswerden möchtest, kompilier mal mit -E . Damit 
wird nur der Präprozessor ausgeführt. Schreib den output in ein neues 
C-File, und lass das dann normal compilieren. Damit bekommst du ein 
Assemblerlisting mit aufgelösten #defines (Aber dann ALLEN). Schön ist 
aber anders...

Oliver

von Fabian B. (fabs)


Lesenswert?

ARGL

Das kommt davon wenn man zu später Stunde ohne ausreichend Kaffee 
programmiert... das müsste natürlich ein | (oder) sein.
Jetzt macht das .lss auch Sinn!
1
            tx_temp.id.std = ((U16)(NID_CONFIG << 7) | (U16)EXT_BIT_MSK) | (U16)ownID;
2
    192c:  80 64         ori  r24, 0x40  ; 64
3
    192e:  8b 83         std  Y+3, r24  ; 0x03
4
    1930:  1c 82         std  Y+4, r1  ; 0x04

Unteres Byte wird die Maske und ownID, das obere 0x00 aus Null-Register.

Gruß
Fabian

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.