Case of Optimierung

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
HobbyProgrammer
Beiträge: 166
Registriert: Di 29. Okt 2019, 12:51
Wohnort: Deutschland , Baden-Württemberg

Case of Optimierung

Beitrag von HobbyProgrammer »

Hallo an alle,

ich bin, auch wenn die Zeit gerade sehr knapp ist, bin ich immernoch daran mein Z180SoftSystem weiter zu Entwickeln und zu Optimieren.

Dazu hätte ich nun  eine Frage zu dem Codeteil welcher die einzelnen Z180 OP-Codes emuliert.

Derzeit wird das ganze über Case of abgebildet. Meine Frage wäre nun, wird das ganze dann als (schnelle) Jump-Table übersetzt? Und wenn ja, evtl. nur wenn alle Case Fälle sauber numerisch aufeinander folgen?

Code: Alles auswählen

opCode: byte;
....
case (opCode) of
    $00: // Befehl
    $01: // Befehl
    $02: // Befehl
    ....
    $FF: // Befehl
Was wird übersetzt wenn Unterbrechungen da sind?

Code: Alles auswählen

opCode: byte;
....
case (opCode) of
    ....
    $1C: // Befehl
    $1D: // Befehl
    $1E: // Befehl
    $1F: // Befehl
    $28: // Befehl
    $29: // Befehl
    ....
Gibt es in Free Pascal auch eine Möglichkeit ein Array mit Pointern auf die Befehlsprozeduren zu erstellen, so wie das in C möglich ist?

Grüße
HobbyProgrammer
Host: Core i7-12700H, NVIDIA RTX3050 6GB, 32GB Ram, 1TB NVME SSD mit KUbuntu 22.04LTS 64bit , VM KUbuntu 22.04 LTS 64bit mit Lazarus 2.2.6 und Cross-Platform Compiler für Linux 32/64bit und Windows 32/64bit. Wine für erste Tests der Windows Binarys.

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Case of Optimierung

Beitrag von Warf »

HobbyProgrammer hat geschrieben:
So 9. Mai 2021, 12:43
Gibt es in Free Pascal auch eine Möglichkeit ein Array mit Pointern auf die Befehlsprozeduren zu erstellen, so wie das in C möglich ist?
Klar

Code: Alles auswählen

type
  TMyProcedure = procedure(param: Integer);

procedure proc1(param: Integer);
begin

end;

procedure proc2(param: Integer);
begin

end;

const
  jumpTable: array[0..1] of TMyProcedure = (@proc1, @proc2);

begin
  jumpTable[0](42);
end.   

HobbyProgrammer
Beiträge: 166
Registriert: Di 29. Okt 2019, 12:51
Wohnort: Deutschland , Baden-Württemberg

Re: Case of Optimierung

Beitrag von HobbyProgrammer »

Danke, für die Info mit der Jumptable.
Was die Sache mit den case of Fällen angeht will ich die Tage einmal ein paar kleine Testprojekte anlegen und mir das Assembler Listing dazu dann anschauen.
Wenn der Compiler die case of Struktur evtl. als Jumptable hinoptimiert wäre mir das am liebsten.
Host: Core i7-12700H, NVIDIA RTX3050 6GB, 32GB Ram, 1TB NVME SSD mit KUbuntu 22.04LTS 64bit , VM KUbuntu 22.04 LTS 64bit mit Lazarus 2.2.6 und Cross-Platform Compiler für Linux 32/64bit und Windows 32/64bit. Wine für erste Tests der Windows Binarys.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Case of Optimierung

Beitrag von Socke »

HobbyProgrammer hat geschrieben:
Mo 10. Mai 2021, 12:48
Was die Sache mit den case of Fällen angeht will ich die Tage einmal ein paar kleine Testprojekte anlegen und mir das Assembler Listing dazu dann anschauen.
Wenn der Compiler die case of Struktur evtl. als Jumptable hinoptimiert wäre mir das am liebsten.
Zumindest auf -O1 und -O2 scheint das nicht der Fall zu sein. Das sind nur eine Reihe von Vergleichen und bedingten Sprüngen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

HobbyProgrammer
Beiträge: 166
Registriert: Di 29. Okt 2019, 12:51
Wohnort: Deutschland , Baden-Württemberg

Re: Case of Optimierung

Beitrag von HobbyProgrammer »

Ich habe nun die CPU Unit in ein einfachen Projekt kopiert und dort mal im Release Mode (-O3) kompiliert.
Diese case of Struktur:

Code: Alles auswählen

procedure TZ180Cpu.execOp00Codes;
...
case (opCode) of
        $00: begin   // OP-Code 0x00 : NOP
            machineCycles := 1;
            clockCycles := 3;
        end;
        $01: begin   // OP-Code 0x01 : LD BC,nn;
            regBC.C := memRead(regPC.Value);
            Inc(regPC.Value);
            regBC.B := memRead(regPC.Value);
            Inc(regPC.Value);
            machineCycles := 3;
            clockCycles := 9;
        end;
        $02: begin   // OP-Code 0x02 : LD (BC),A
            memWrite(regBC.Value, regAF.A);
            machineCycles := 3;
            clockCycles := 7;
        end;
        $03: begin   // OP-Code 0x03 : INC BC
            Inc(regBC.Value);
            machineCycles := 2;
            clockCycles := 4;
        end;
        $04: begin   // OP-Code 0x04 : INC B
            inc8Bit(regBC.B);
            machineCycles := 2;
            clockCycles := 4;
        end;
        $05: begin   // OP-Code 0x05 : DEC B
            dec8Bit(regBC.B);
            machineCycles := 2;
            clockCycles := 4;
        end;
        $06: begin   // OP-Code 0x06 : LD B,n
            regBC.B := memRead(regPC.Value);
            Inc(regPC.Value);
            machineCycles := 2;
            clockCycles := 6;
        end;  
        .... 
        $FE: begin   // OP-Code 0xFE : CP n
            cpA8Bit(memRead(regPC.Value));
            Inc(regPC.Value);
            machineCycles := 2;
            clockCycles := 6;
        end;
        $FF: begin   // OP-Code 0xFF : RST 38H
            push(regPC.Value);
            regPC.Value := $0038;
            machineCycles := 5;
            clockCycles := 11;
        end;
end;                                 
        
wird wie folgt übersetzt:

Code: Alles auswählen

.section .data.rel.n_Z180_CPU$_$TZ180CPU_$__$$_EXECOP00CODES
	.balign 4
.Ld1:
	.long	.Lj103-.Ld1
	.long	.Lj104-.Ld1
	.long	.Lj105-.Ld1
	.long	.Lj106-.Ld1
	.long	.Lj107-.Ld1
	.long	.Lj108-.Ld1
	.long	.Lj109-.Ld1
	.long	.Lj110-.Ld1
	.long	.Lj111-.Ld1
	...
	.long	.Lj354-.Ld1
	.long	.Lj355-.Ld1
	.long	.Lj356-.Ld1
	.long	.Lj357-.Ld1
	.long	.Lj358-.Ld1
Das sieht sehr nach einer JumpTable aus. Diese case of Struktur ist auch sauber von $00 - $FF ausgefüllt.

Dagegen wird diese case of Struktur:

Code: Alles auswählen

procedure TZ180Cpu.execOpDdCodes;
    ...
    case (opCode) of
        $09: begin   // OP-Code 0xDD09 : ADD IX,BC
            addIX16Bit(regBC.Value);
            machineCycles := 6;
            clockCycles := 10;
        end;
        $19: begin   // OP-Code 0xDD19 : ADD IX,DE
            addIX16Bit(regDE.Value);
            machineCycles := 6;
            clockCycles := 10;
        end;
        $21: begin     // OP-Code 0xDD21 : LD IX,nn
            regIX.low := memRead(regPC.Value);
            Inc(regPC.Value);
            regIX.high := memRead(regPC.Value);
            Inc(regPC.Value);
            machineCycles := 4;
            clockCycles := 12;
        end;
        $22: begin   // OP-Code 0xDD22 : LD (nn),IX
            tmpWord.low := memRead(regPC.Value);
            Inc(regPC.Value);
            tmpWord.high := memRead(regPC.Value);
            Inc(regPC.Value);
            memWrite(tmpWord.Value, regIX.low);
            memWrite(tmpWord.Value + 1, regIX.high);
            machineCycles := 7;
            clockCycles := 19;
        end;
        $23: begin   // OP-Code 0xDD23 : INC IX
            Inc(regIX.Value);
            machineCycles := 3;
            clockCycles := 7;
        end;
        $29: begin   // OP-Code 0xDD29 : ADD IX,IX
            addIX16Bit(regIX.Value);
            machineCycles := 6;
            clockCycles := 10;
        end;
        $2A: begin   // OP-Code 0xDD2A : LD IX,(nn)
            tmpWord.low := memRead(regPC.Value);
            Inc(regPC.Value);
            tmpWord.high := memRead(regPC.Value);
            Inc(regPC.Value);
            regIX.low := memRead(tmpWord.Value);
            regIX.high := memRead(tmpWord.Value + 1);
            machineCycles := 6;
            clockCycles := 18;
        end;                                               
        ...
so übersetzt:

Code: Alles auswählen

.section .text.n_z180_cpu$_$tz180cpu_$__$$_execopddcodes
	.balign 16,0x90
.globl	Z180_CPU$_$TZ180CPU_$__$$_EXECOPDDCODES
	.type	Z180_CPU$_$TZ180CPU_$__$$_EXECOPDDCODES,@function
Z180_CPU$_$TZ180CPU_$__$$_EXECOPDDCODES:
.Lc95:
# [3869] begin
	pushq	%rbx
	pushq	%r12
	leaq	-8(%rsp),%rsp
.Lc97:
# Var opCode located in register al
# Var memOffset located in register r12b
# Var tmpByte located at rsp+0, size=OS_8
# Var tmpWord located at rsp+4, size=OS_16
	movq	%rdi,%rbx
# Var $self located in register rbx
# [3870] opCode := readOpCode(regPC.Value);
	movzwl	48(%rbx),%eax
# Var $self located in register rbx
	movq	%rbx,%rax
	movl	16(%rax),%ecx
	movl	24(%rax),%edx
	addl	%ecx,%edx
	movl	%edx,16(%rax)
# Var opCode located in register al
# [3871] Inc(regPC.Value);
	addw	$1,48(%rbx)
# [3872] case (opCode) of
	cmpb	$9,%al
	jb	.Lj749
	subb	$9,%al
	je	.Lj750
	subb	$16,%al
	je	.Lj751
	subb	$8,%al
	je	.Lj752
	subb	$1,%al
	je	.Lj753
	subb	$1,%al
	je	.Lj754
	subb	$6,%al
	je	.Lj755
	subb	$1,%al
	je	.Lj756
	subb	$1,%al
	je	.Lj757
	subb	$9,%al
	je	.Lj758
	...
das scheint einfach eine Reihe von vergleichen zu sein.

Wenn ich jetzt jede case of Struktur mit 256 (opCode ist vom Typ Byte) Abfragen erstelle sollte doch dann immer eine JumpTable herauskommen, oder sehe ich das falsch?

Wäre auf jeden fall einfacher zu lösen als die ganze Unit als JumpTable umzuschreiben.

Grüße
HobbyProgrammer
Host: Core i7-12700H, NVIDIA RTX3050 6GB, 32GB Ram, 1TB NVME SSD mit KUbuntu 22.04LTS 64bit , VM KUbuntu 22.04 LTS 64bit mit Lazarus 2.2.6 und Cross-Platform Compiler für Linux 32/64bit und Windows 32/64bit. Wine für erste Tests der Windows Binarys.

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Case of Optimierung

Beitrag von PascalDragon »

HobbyProgrammer hat geschrieben:
Mo 10. Mai 2021, 17:51
Wäre auf jeden fall einfacher zu lösen als die ganze Unit als JumpTable umzuschreiben.
Der Compiler ermittelt selbst, ob eine JumpTable oder eine Liste linearer Vergleiche besser ist. Und was genau er macht ist ein Detail der Implementierung, das heißt, das kann sich von Version zu Version und von Platform zu Platform sowie abhängig von den aktivierten Optimierungen ändern.
Kümmere dich also besser erstmal nicht darum (ich persönlich sehe das als Mikroptimierung an), sondern schau, ob du anderweitig Optimierungspotential hast, das nicht von der Codegenerierung abhängt.
FPC Compiler Entwickler

Antworten