Autor Thema: Calling-Conventions  (Gelesen 7398 mal)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« am: 11. January 2010, 20:07 »
Hallo,


ich hab mal drei Fragen zu Calling-Conventions. Jene möchte ich gerade für meine Plattform spezifizieren und bin da auf ein paar Problemchen gestoßen.

1. Wenn eine Funktion sehr viele Parameter, oder eine variable Anzahl an Parametern, hat dann können die ja nicht mehr alle per Register übertragen werden. Dafür gibt es 2 Möglichkeiten, entweder alle überzähligen bzw. variablen Parameter der Reihe nach auf den Stack legen (an Top-of-Stack ähnlich cdecl), wobei dann wieder geklärt werden muss wer den Stack frei gibt (am besten der Aufrufer dann funktionieren auch variable Parameter), oder alle überzähligen Parameter in einen passenden Speicherbereich legen (was normalerweise auch der Stack ist aber eben nicht Top-of-Stack sondern irgendwo im Stack-Frame des Callers) und die Adresse dieses Bereichs als unsichtbares Parameter im Register übergeben (also wie FastCall). Die zweite Variante hat den Nachteil das ein zusätzliches Parameter ein Register belegt und man zum Zugriff auf die zusätzlichen Parameter immer auch eine zusätzliche Info (eben diesen Pointer) benötigt was sicher oft zu einem zusätzlichen Befehl führt und manchmal, falls dieser Pointer mal aus den Registern verdrängt werden muss, sogar ein zusätzlicher Speicherzugriff ist. Ich möchte mich lieber für die erste Variante entscheiden bin mir aber nicht sicher ob ich was übersehen hab. Die FastCall-Variante wird z.B. vom Linux-Kernel (intern und beim SYSCALL) verwendet und ich gehe mal davon aus das die sich dabei was gedacht haben. Gerade auf x86 mit den wenigen Registern muss man sich schon ordentlich Mühe geben damit die Parameterübergabe nicht zum kostspieligen Fiasko wird. Auf meiner Plattform hab ich zwar deutlich mehr Register, so das dieser Schuh nicht so sehr drückt. Welche von beiden Varianten haltet Ihr für besser? Gibt es noch andere Möglichkeiten? Ich möchte dieses Problem schon ordentlich lösen und auch nur eine Calling-Convention haben die alle Anwendungsfälle mit möglichst maximaler Performance abdeckt.

2. Wie sieht das mit dem this-Pointer bei Klassen-Methoden aus? Dazu hab ich kaum Infos gefunden (außer den thiscall von MS und den will ich nicht kopieren). Ich würde diesen unsichtbaren Parameter gerne als erstes im Registerbereich ablegen und den auch bei der Rückkehr dort lassen so das dieses Register gar nicht verändert werden muss wenn Methoden einer Klasse sich gegenseitig aufrufen (was ja nicht unbedingt selten ist), also ein Register exklusiv für diese Verwendung reserviert wird (bei insgesamt 64 Registern tut das nicht weh).

3. Was haltet ihr eigentlich davon kleine Strukturen (bis 32 oder 64 Byte) direkt in den Registern zu übergeben oder würdet ihr einen Pointer bevorzugen?


Ich hoffe ihr habt ein paar Anregungen für mich. Danke schon mal im Voraus.


Grüße
Erik
« Letzte Änderung: 11. January 2010, 22:44 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 11. January 2010, 23:00 »
3. Was haltet ihr eigentlich davon kleine Strukturen (bis 32 oder 64 Byte) direkt in den Registern zu übergeben oder würdet ihr einen Pointer bevorzugen?
Impliziert das nicht auch einen semantischer Unterschied? Call by value gegenüber call by reference? Wobei du by value natürlich auch machen kannst, indem du erstmal kopierst und dann trotzdem einen Pointer übergibst. Klingt einerseits verschwenderischer, hat aber andererseits den Vorteil, dass es einheitlich ist und du keine willkürliche 32-Byte-Grenze hast, ab der es plötzlich anders läuft.

Bei den übrigen Fragen lasse ich ausnahmsweise mal die anderen vor, mir ist heute abend nicht mehr nach nachdenken. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 12. January 2010, 17:51 »
Hallo,


Impliziert das nicht auch einen semantischer Unterschied? Call by value gegenüber call by reference?
Nein, ich meine mit der Frage nur "Call by Value". Bei "Call by Reference" wird immer ein Pointer übergeben und gut is. Bei "Call by Value" für Strukturen wird üblicherweise eine Kopie bei Top-of-Stack abgelegt (so wie die Parameter bei cdecl, bei Klassen wird dafür sogar der Copy-Constructor benutzt) und der Callee weiß eben wo diese Struktur liegt und kann ganz normal relativ zu SP zugreifen. Mir ist da nun die Idee gekommen das man kleine Struckturen, ich denke da z.B. an den Rückgabewert von div() oder an complex, direkt in den Registern zu übergeben/zurückzugeben um das ablegen auf dem Stack und das wieder einlesen zum weiterarbeiten zu ersparen. Register sind ja genug vorhanden. Die Grenze mit 32Byte ist natürlich willkürlich aber irgendwo muss man sie ziehen. Es gibt sicher ne Menge Funktionen die davon profitieren können.
Auf ner Soft-CPU in nem FPGA werd ich wohl kaum über 50MHz hinaus kommen und wenn ich damit wenigsten einem 486 mit 100MHz Paroli bieten möchte muss ich schon ordentlich optimieren, zumindest will ich die ganzen angezogenen Handbremsen der x86-Architektur vermeiden.

Bei den übrigen Fragen lasse ich ausnahmsweise mal die anderen vor
Ja, immer mit den selben Leuten zu diskutieren ist ja auch irgendwann mal langweilig. :-D


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen