Sammelthread zu Visual C++’ Compiler

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon dot » 07.06.2018, 10:59

Schrompf hat geschrieben:bringst Du dort regelmäßig Kuchen vorbei? :-)

Keine Ahnung was ich gemacht hab :D , aber ich hab hier ein sehr langes Textfile mit Bugs, für die ich keine Zeit hatte, einen anständigen Report zu schreiben. Mal sehen, vielleicht machen wir demnächst weitere Experimente... :twisted:
Benutzeravatar
dot
Michael Kenzel
Establishment
 
Beiträge: 1647
Registriert: 06.03.2004, 19:10

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 07.06.2018, 21:47

Ich bin ein kleines Bisschen irritiert :D Ja los, schaufel alles rein …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 09.06.2018, 22:28

yeah

win7.png

(Quelle)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 14.07.2018, 12:11

Aktualisierung auf Visual Studio 15.7.5.

Mit diesem Update wurde ein Haufen Bugs behoben – wirklich eine riesen Menge.

Ansonsten hat sich am Compiler kaum was getan.

Der 32-Bit-Compiler nutzte bei Optimierung auf Größe bisweilen für Ausdrücke wie x - 20 Befehle wie lea eax, eax-20. Nun nutzt er öfter sub eax, 20. Das erzeugt höheren Registerdruck und produziert längere Befehlsketten. Ich denke, dass das eine Regression ist, denn ein vernünftiger Grund fällt mir nicht ein (lea war perfekt dafür).

Der 64-Bit-Compiler fasst Sprünge weniger aggressiv zusammen, wenn auf Geschwindigkeit optimiert wird:

  if(…) return 0; else return var;

kompiliert nun zu

  xor eax,eax
  add rsp,70h
  pop r15
  pop r14
  pop r13
  pop r12
  pop rdi
  pop rsi
  pop rbx
  ret
  mov rax,qword ptr [rax+8]
  add rsp,70h
  pop r15
  pop r14
  pop r13
  pop r12
  pop rdi
  pop rsi
  pop rbx
  ret


Vorher wurde das zusammengefasst.

Insgesamt sind die Leistungsänderungen kaum messbar. Alle meine Projekte zusammen haben sich weniger als 300 B zum Schlechtne verändert – bei einigen Megabyte Gesamtgröße. Dafür, wie gesagt, haufenweise Bugfixes.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 17.08.2018, 17:32

Aktualisierung auf 15.8.0.

Die Code Generation nutzt eine neue Version der Static Single Assignment Form. Ich habe bisher keine offizielle Stellungnahme gefunden, aber ein Mitglied des DirectX-Teams schreibt es.

Eine erste Sichtung bestätigt das: Der Code meiner Projekte hat sich drastisch verändert. Auf den ersten Blick würde ich sagen: Unter x86-32 gibt es fantastische Verbesserungen und unter x86-64 fatale Verschlechterungen.

Unter x86-32 sind vor allem Funktionen betroffen, die aus etlichen else if oder switch mit unzähligen case bestehen. Ich beobachte hier teils 20 % weniger Befehle(!) und falls sich das bestätigt, ist das echt sensationell. In die augenscheinlichen Verschlechterungen von x64 habe ich noch nicht reingeschaut.

Mehr dazu später.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 17.08.2018, 20:41

x86-32 mit Optimierung auf Größe:

Ich habe mir nun mal das Disassembly einer Funktion angeschaut, die von 1483 B auf 1131 B geschrumpft ist (sage und schreibe 24 %!) …

Optimiert wurden keinesfalls triviale switch-Konstruktionen à:

  switch(param) {
    case 0: return "foo";
    case 1: return "bar";
    case 2: …


… die waren auch vorher schon ziemlich optimal. Nein, stattdessen werden jetzt verschiedene Unterzweige zusammengefasst. Sagen wir etwa:

  char buf[…];
  switch(errorType) {

    case CError:
      strcpy(buf, strerr(errno));
      break;

    case CppException:
      strcpy(buf, exceptionObject.str());
      break;

    default:
      strcpy(buf, "unknown");
      break;


Obwohl die drei Pfade in der SSA drastisch unterschiedlich sind (einer erzeugt einen C++-String, dar andere nicht, und der dritte läuft komplett über Konstanten) wird bei mir nun das strcpy() zusammengefasst. Wenn man bedenkt, dass es zuvor völlig unterschiedliche Register und völlig unterschiedliche Stack-Quellen benutzt hat, ist das schon recht beeindruckend. Ich sehe sogar Code, der bei Strings die Null am Ende mitkopiert, mit Code zusammengefaltet, der sie überspringt, wenn genug überlappende Befehle existieren. Es fällt mir zunehmend schwer, da etwas Negatives festzustellen.


x86-64 mit Optimierung auf Größe:

Sprach ich von Negativem? Ach ja, die 64-Bit-Version. Dieser Code:

  auto end = copyUntilZero(message, "D3D info: GPU supports Direct3D ");
  switch(USize(more)) {
    case 0x9100: copyZeroTerminated(end, "9" ); break;
    case 0x9200: copyZeroTerminated(end, "9.0b"); break;
    case 0x9300: copyZeroTerminated(end, "9.0c"); break;
    case 0xA000: copyZeroTerminated(end, "10" ); break;
    case 0xA100: copyZeroTerminated(end, "10.1"); break;
    case 0xB000: copyZeroTerminated(end, "11" ); break;
    case 0xB100: copyZeroTerminated(end, "11.1"); break;
    case 0xB200: copyZeroTerminated(end, "11.2"); break;
    case 0xB300: copyZeroTerminated(end, "11.3"); break;
    case 0xB400: copyZeroTerminated(end, "11.4"); break;
    case 0xC000: copyZeroTerminated(end, "12" ); break;
    case 0xC100: copyZeroTerminated(end, "12.1"); break;
    default: UNREACHABLE;
  }


… sieht auf x86 für Größe perfekt aus:
Code: Ansicht erweitern :: Alles auswählen
mov         edx,offset string "D3D info: GPU supports Direct3D@"... (01C1E0h)
lea         ecx,[message]
mov         al,44h
cbw
mov         word ptr [ecx],ax
add         ecx,2
inc         edx
mov         al,byte ptr [edx]
test        al,al
jne         UTF16Supervisor::forwardEventAsUTF16+2FEh (015E35h)
mov         eax,0B100h
cmp         edi,eax
ja          UTF16Supervisor::forwardEventAsUTF16+382h (015EB9h)
je          UTF16Supervisor::forwardEventAsUTF16+378h (015EAFh)
cmp         edi,9100h
je          UTF16Supervisor::forwardEventAsUTF16+36Eh (015EA5h)
cmp         edi,9200h
je          UTF16Supervisor::forwardEventAsUTF16+364h (015E9Bh)
cmp         edi,9300h
je          UTF16Supervisor::forwardEventAsUTF16+35Ah (015E91h)
cmp         edi,0A000h
je          UTF16Supervisor::forwardEventAsUTF16+350h (015E87h)
mov         edx,offset string "11" (01C224h)
mov         eax,offset string "10.1" (01C21Ch)
cmp         edi,0A100h
cmove       edx,eax
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "10" (01C218h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "9.0c" (01C210h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "9.0b" (01C208h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "9" (01C204h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "11.1" (01C228h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
cmp         edi,0B200h
je          UTF16Supervisor::forwardEventAsUTF16+3C0h (015EF7h)
cmp         edi,0B300h
je          UTF16Supervisor::forwardEventAsUTF16+3B6h (015EEDh)
cmp         edi,0B400h
je          UTF16Supervisor::forwardEventAsUTF16+3ACh (015EE3h)
mov         edx,offset string "12.1" (01C24Ch)
mov         eax,offset string "12" (01C248h)
cmp         edi,0C000h
jmp         UTF16Supervisor::forwardEventAsUTF16+348h (015E7Fh)
mov         edx,offset string "11.4" (01C240h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "11.3" (01C238h)
jmp         UTF16Supervisor::forwardEventAsUTF16+442h (015F79h)
mov         edx,offset string "11.2" (01C230h)

53 Befehle, und dazu recht kompakte. Jetzt die x64-Version mit den selben Einstellungen, ebenfalls für Größe(!) optimiert:

Code: Ansicht erweitern :: Alles auswählen
lea         rdx,[string "D3D info: GPU supports Direct3D@"... (01E530h)] 
mov         al,44h 
lea         rcx,[message] 
inc         rdx 
movsx       eax,al 
mov         word ptr [rcx],ax 
add         rcx,2 
mov         al,byte ptr [rdx] 
test        al,al 
jne         UTF16Supervisor::forwardEventAsUTF16+3DFh (0168A7h) 
mov         eax,0B100h 
cmp         r8,rax 
ja          UTF16Supervisor::forwardEventAsUTF16+535h (0169FDh) 
je          UTF16Supervisor::forwardEventAsUTF16+50Fh (0169D7h) 
cmp         r8,9100h 
je          UTF16Supervisor::forwardEventAsUTF16+4E9h (0169B1h) 
cmp         r8,9200h 
je          UTF16Supervisor::forwardEventAsUTF16+4C3h (01698Bh) 
cmp         r8,9300h 
je          UTF16Supervisor::forwardEventAsUTF16+49Dh (016965h) 
mov         eax,31h 
mov         word ptr [rcx],ax 
cmp         r8,0A000h 
je          UTF16Supervisor::forwardEventAsUTF16+47Fh (016947h) 
cmp         r8,0A100h 
je          UTF16Supervisor::forwardEventAsUTF16+461h (016929h) 
lea         rdx,[string "11" (01E574h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+44Ah (016912h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "10.1" (01E56Ch)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+468h (016930h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "10" (01E568h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+486h (01694Eh) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
mov         eax,39h 
lea         rdx,[string "9.0c" (01E560h)] 
mov         word ptr [rcx],ax 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+4ACh (016974h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
mov         eax,39h 
lea         rdx,[string "9.0b" (01E558h)] 
mov         word ptr [rcx],ax 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+4D2h (01699Ah) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
mov         eax,39h 
lea         rdx,[string "9" (01E554h)] 
mov         word ptr [rcx],ax 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+4F8h (0169C0h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
mov         eax,31h 
lea         rdx,[string "11.1" (01E578h)] 
mov         word ptr [rcx],ax 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+51Eh (0169E6h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
mov         eax,31h 
mov         word ptr [rcx],ax 
cmp         r8,0B200h 
je          UTF16Supervisor::forwardEventAsUTF16+5DDh (016AA5h) 
cmp         r8,0B300h 
je          UTF16Supervisor::forwardEventAsUTF16+5BFh (016A87h) 
cmp         r8,0B400h 
je          UTF16Supervisor::forwardEventAsUTF16+5A1h (016A69h) 
cmp         r8,0C000h 
je          UTF16Supervisor::forwardEventAsUTF16+583h (016A4Bh) 
lea         rdx,[string "12.1" (01E59Ch)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+56Ch (016A34h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "12" (01E598h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+58Ah (016A52h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "11.4" (01E590h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+5A8h (016A70h) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "11.3" (01E588h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+5C6h (016A8Eh) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 
lea         rdx,[string "11.2" (01E580h)] 
inc         rdx 
lea         rcx,[rcx+2] 
movsx       eax,byte ptr [rdx] 
mov         word ptr [rcx],ax 
cmp         byte ptr [rdx],r14b 
jne         UTF16Supervisor::forwardEventAsUTF16+5E4h (016AACh) 
jmp         UTF16Supervisor::forwardEventAsUTF16+691h (016B59h) 

140 Befehle. Und alles Duplikate. Über drei Mal so groß wie die 32-Bit-Version. DREI FUCKING MAL. Ohne Grund, einfach because FUCK YOU, that’s why. I can’t even …

Wirklich, vergesst den 64-Bit-Compiler, wenn ihr nach Größe geht. Und das ist so eine riesen Verschwendung, denn theoretisch könnte die 64-Bit-Version durch geringeren Registerdruck sogar überlegenen Code produzieren. Ich habe das Gefühl, dass das noch nie jemand bei Microsoft getestet hat und wenn ich’s isoliert kriege, wird das ein fetter Bug Report.

Davon ab hat sich mal wieder die Gewichtung für Inlining geändert. Das hatten wir doch letztens erst. Warum mit jedem Release? Und es wird irgendwie auch überhaupt nicht besser dadurch …

… okay, auch mal was Positives: Die Register Allocation hat sich wirklich verbessert. In der Größenordnung von 1 % sogar, also deutlich über dem Rauschen, in dem die letzten Verbesserungen untergingen. Ich sehe weniger MOV und weniger Registerdruck gerade bei vielen Integer-Operationen. Sehr vereinzelt, aber in der Summe mit stattlichem Ergebnis.


x86 und x64 für Geschwindigkeit:

Habe leider gerade keine Benchmarks parat. Die Größe der Executables ist jedenfalls leicht gesunken – viel weniger Befehle (wahrscheinlich die gleichen SSA-Verbesserungen wie bei der Optimierung für Größe), aber dafür mehr statische Daten (?!?). Hmm.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 21.08.2018, 19:07

Bug Report für die absurde Größe auf x64 ist raus: https://developercommunity.visualstudio ... r-siz.html
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 21.08.2018, 22:44

Wieder was gelernt: Wenn die Funktionen zu groß werden, schaltet sich Visual C++’ Optimizer einfach ab. Mit /d2OptimizeHugeFunctions als zusätzlichem Parameter zwingt man ihn.

In meinen Projekten habe ich so eine Funktionsgröße nie erreicht, und auch bei Assimp konnte ich das jetzt nicht reproduzieren. Aber wer mit sowas zu tun hat, weiß nun bescheid.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 29.08.2018, 10:22

Wenn ihr z.B. float zu int konvertiert, ist reinterpret_cast undefiniertes Verhalten (obwohl Visual C++ es frisst) und union ebenfalls (obwohl GCC/Clang es fressen). Um standardkonform zu sein, solltet ihr das via memcpy() machen (ist übrigens auch in allen aktuellen Compilern die am besten optimierte Methode).

Nun, falls ihr das auch bei anderen Datentyp-Aliasing-Problemen so macht, habe ich schlechte Nachrichten für euch …

The bug occurs in code as simple as this:

unsigned char A[2] = {0xAB, 0xCD};
int target = -1;
memcpy(&target, A, 2);
printf("%x\n", target);


When targeting x64, compiling without optimizations yields ffffcdab. Compiling with optimizations yields cdab. We appear to be giving the wrong address to the cpblk .NET instruction.
Äh, wartet. Euer Compiler ist auf .NET gebaut?!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 23.09.2018, 13:40

Wieder was Interessantes: __builtin_nan().

Ausgangslage: C++ hat std::numeric_limits<double>::quiet_nan() & Co. Damit bekommt man NaN, Infinity, usw zurückgeliefert.

Weiterhin: C++ erfordert, dass diese Funktionen constexpr sind.

Außerdem: C++ verbietet NaN & Co. in constexpr.

Merkt ihr den Widerspruch? Alle klassischen Möglichkeiten, einer Variable NaN zuzuweisen, schlagen mit constexpr fehl:

  constexpr double nan = 1.0 / 0.0; // Visual C++: error C2131: expression did not evaluate to a constant     note: failure was caused by an undefined arithmetic operation
  constexpr double nan = 1.7976931348623157e308 * 2 * 0; // GCC: error: is not a constant expression


Aufgelöst werden kann das nur durch Compiler-interne Konstanten, auf die die Standardbibliothek für std::numeric_limits zurückgreift.

Bei GCC ist das z. B. __builtin_nan(). Und bei Visual C++ … seit neuestem ebenso:

  _NODISCARD static constexpr double quiet_NaN() noexcept
  { // return non-signaling NaN
  return (__builtin_nan("0"));
  }


Wer nun glaubt, __builtin_nan("0") direkt nutzen zu können, irrt:

  constexpr nan = __builtin_nan("0"); // error C2143: syntax error: missing ')' before ';'

Aus irgendwelchen Compiler-internen Gründen darf die Funktion nur aus einer anderen Funktion aufgerufen werden:

  constexpr double make_nan() {
    return __builtin_nan("0");
  }
  constexpr double nan = make_nan();


Das ist … exotisch. Wisst’r bescheid. Ich hoffe nun darauf, dass Microsoft das Builtin weiter entwickelt, so dass ich den Quelltext für VC++ und Clang/GCC irgendwann mal vereinigen kann …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: Sammelthread zu Visual C++’ Compiler

Beitragvon Krishty » 07.11.2018, 19:16

Aktualisierung von Visual C++ 2018.8.7 auf 2018.8.9 … keine Änderungen am C++-Compiler.

Warum ich’s dann überhaupt installiert habe? Weil geschrieben wurde, dass Visual C++ 2018.9 große Verbesserungen in der Template-Konformität bringt: Use the official range-v3 with MSVC 2017 version 15.9

Der Artikel spornt zum Ausprobieren an, aber leider ist 2018.9 noch gar nicht released. Und im kack Visual Studio Installer sieht man ja immer erst nach dem Update, welche Version man kriegt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6661
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Vorherige

Zurück zu Programmiersprachen, Quelltext und Bibliotheken

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast