Zeilenlängenbug (Windows-Exklusiv?)

Begonnen von Arnold, Dienstag, 7. Januar 2020, 14:59

« vorheriges - nächstes »

Arnold

Hallo zusammen,

während ich eine Partitur abschreibe, und dabei (noch) nicht alle Stimmen (eigentlich Staves) gemeinsam enden, dann ergibt sich hin und wieder, daß »ein System irgendwo in der Mitte« verkürzt ist.
Wenn alles komplett ist (alle Stimmen bis zum Ende des Stücks reichen) ist dieses Problem bis jetzt immer verschwunden.

Es tritt unter Win7 mit der 2.19.83 auf, nicht aber unter Linux (in der VM).
Es sieht mir nach einer Folge des »beliebten« X87-Coprozessor-Spiels »Vergleiche (mit 80 Bit Genauigkeit) neu berechnete Fließkommazahl mit gepeicherter Fließkommazahl (die bereits auf 64 Bit Genauigkeit gerundet ist)« aus.

Ein Minimalbeispiel zu erstellen ist müßig, da es kaum richtig klein sein kann.
Daher anbei erst einmal nur Bilder der Seiten 4 und 5 - auf Seite 4 ist das zweite System zu kurz geraten, auf Seite drei waren noch so viele Notenzeilen enthalten, daß die ganze Seite mit einem System gefüllt war.

Und eigentlich eine Strategiefrage für die Windows-Version:
  • Wäre es nicht Zeit für eine weitere Windows-Version?
  • Entweder eine 64-Bit-Version, welche die dort übliche SSE-Arithmetikeinheit statt der X87-FPU benutzt?
  • Oder eine zweite 32-Bit-Version, welche aber statt der X87-FPU auch die (angeblich schnellere) SSE2 benutzt, dabei aber Pentium4 als minimale Prozesserarchitektur voraussetzt?
  • Oder ist die Nachfrage nach der Windows-Version von LILYPOND gar schon so gering, daß eine Weiterentwicklung der Zielarchitektur den Aufwand nicht lohnt?
Mit der anderen FPU unter Windows könnte man zumindest effektiv vergleichen, ob solche Probleme von der FPU-Genauigkeitseinstellung oder ähnlichen FPU-Eigenheiten herrühren.

Arnold

harm6

#1
Zitat von: Arnold am Dienstag,  7. Januar 2020, 14:59
Und eigentlich eine Strategiefrage für die Windows-Version:
  • Wäre es nicht Zeit für eine weitere Windows-Version?
  • Entweder eine 64-Bit-Version, welche die dort übliche SSE-Arithmetikeinheit statt der X87-FPU benutzt?
  • Oder eine zweite 32-Bit-Version, welche aber statt der X87-FPU auch die (angeblich schnellere) SSE2 benutzt, dabei aber Pentium4 als minimale Prozesserarchitektur voraussetzt?
  • Oder ist die Nachfrage nach der Windows-Version von LILYPOND gar schon so gering, daß eine Weiterentwicklung der Zielarchitektur den Aufwand nicht lohnt?
Mit der anderen FPU unter Windows könnte man zumindest effektiv vergleichen, ob solche Probleme von der FPU-Genauigkeitseinstellung oder ähnlichen FPU-Eigenheiten herrühren.

Soweit ich das sagen kann so werden windows- (und auch mac-) Versionen im wesentlichen ungeprüft released.
Schon allein weil es problematisch ist sie zu testen, wie Du ja weißt.

Ich glaube nicht, daß das Interesse an windows-Versionen gering ist. Auch 64-bit-Versionen werden immer wieder angemahnt.

Allerdings scheint niemand dran zu arbeiten.

Du könntest natürlich auf devel noch mal nachfragen, wieder mit Verweis auf Deine bereits geleistet Vorarbeit.

Ich würde aber zumindest abwarten bis
https://www.uni-mozarteum.at/de/kunst/notensatz-konferenz.php
vorbei ist.
Da ist mit Sicherheit für einige im Moment der Fokus.

Ich selbst steh auch im Programm, aber mit windows kann ich ohnehin nicht helfen.

Oder vielleicht sogar:
Willst Du nicht auch dort sprechen, über eben diesen Problemkreis?
Das würde das Problem doch recht nachdrücklich betonen.
Ich weiß, es ist schon nächste Woche aber Han-Wen ist auch erst vor wenigen Tagen dazu gestoßen.
Falls das für Dich in Frage kommt könntest Du Werner fragen, Kontakt-Adresse auf der verlinkten Seite


Gruß,
  Harm

Arnold

Nun, Harm,

an dem Wochende habe ich keine Zeit um nach Salzburg zu kommen (sondern eine Klarinettenensemble-Probe).

Ein Problem der "Fremdsysteme" ist auch, daß die Entwicklung quasi exclusiv im Lilydev geschieht, in einer VM-Umgebung mit Linux drin.
Die Windows-Version wird (zusammen mit anderem) im GUB erstellt. Und dazu gibt es leider kein vorbereitetes VM-Image, in welches man »nur« den Tarball entpackt und dann (ohne Netzverbindung) die Windows-Version von Lilypond (und Guile, und eventuell noch weiteres wie z. Bsp. Gostscript, auf [das alte] Lilypad kann ich ja verzichten) erstellt.

Arnold

Arnold

#3
HEUREKA! Workaround gefunden!

Ich fand:
  • Guile kann DLLs laden, darin eine Prozedur nach gegebenem Funktionsnamen suchen und ausführen
  • MINGW kann diese DLLs erstellen, und unterstützt in einer Prozedur Inline-Assembler-Code

So bildete ich das Initialisieren des X87-Controlwortes, zu dem die Bibliotheksfunktion _FPU_SETCW() in MINGW nicht mehr enthalten ist, als Prozedur im Assemblercode nach:

/* FILE: x87mant53.c
* compile to DLL with MINGW installed in LILYDEV:
*
*    i686-w64-mingw32-gcc -shared -ox87mant53.dll x87mant53.c
*
*/

void X87mantissa53(void)
{
  asm(
      "     push %ebp"
    "\n     mov  $0x027f, %eax"
    "\n     push %eax"
    "\n     mov  %esp, %ebp"
    "\n     fldcw (%ebp)"
    "\n     pop %eax"
    "\n     pop %ebp"
  );
}


In den LY-Dateien mit den mutmaßlichen Precission-Missmatch-Seiteneffekten fügte ich hinzu, umd die DLL vom lokalen Arbeitsverzeichnis zu laden und die Prozedur darin auszuführen:

%{
  For Windows usage only:

  Insert the following SCHEME lines near the beginning of your LY file,
  which is targeted to be compiled by LILYPOND 2.19.80 or newer - until
  the missing FPU_SETCW in MINGW is fixed.

  Or put it into 'init.ly' of your LILYPOND installation.

  BUT: check the path to this DLL!

%}

#(let ((dynlib (dynamic-link "./x87mant53.dll")))
  (ly:progress "\nGoing to set X87 mantissa length via DLL procedure ... ")
  (dynamic-call "X87mantissa53" dynlib)
  (ly:progress "done.\n"))



Damit wurde sowohl dieses Zeilenlängenproblem als auch die Seitenzahl-zu-groß-Assertion https://lilypondforum.de/index.php/topic,489.0.html (in je einem Beispiel getestet) behoben.

Folglich könnte man diesen Inline-Assembler-Code auch in main.cc in die Dummy-Version von configure_fpu() einfügen, eventuell in dieser Weise:

#include <fpu_control.h>
static void
configure_fpu ()
{
   fpu_control_t fpu_control = 0x027f;
   _FPU_SETCW (fpu_control);
}
 
#else
 
static void
configure_fpu ()
{
+#if ((defined (__x86__) || defined (__i386__)) \
+  && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__))
+  /* If this is a MINGW compilation (for Windows),
+   * but not using SSE2 arithmetic unit nor is a 64 bit compilation (which uses SSE2 by default)
+   * _FPU_SETCW() got lost in the MINGW libraries!
+   * Here is an inline assembler call to execute the same task for the X87 arithmetic unit.
+   *
+   * TODO: output a message what we're going to do here, _only_ if DEBUG level is selected,
+   *       but configure_fpu() is called before the commandline options get evaluated.
+   *       At the moment the info message will be blanked on the console, but if output is
+   *       directed into a file, most editors will show it.
+   */
+  fprintf(stderr, " X87 FPU setup via asm() ... ")
+  asm(
+      "     push %ebp"
+    "\n     mov  $0x027f, %eax"
+    "\n     push %eax"
+    "\n     mov  %esp, %ebp"
+    "\n     fldcw (%ebp)"
+    "\n     pop %eax"
+    "\n     pop %ebp"
+  );
+  fprintf(stderr, "done.\r                                 \r");
+#endif /* (defined(__x86__) || defined(__i386__)) && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__) */
}

#endif /* defined(__x86__) || defined(__i386__) */
(Änderung 2020-01-13: fprintf-Meldung optimiert)

Arnold


harm6

Glückwunsch !!

Zitat
In den LY-Dateien mit den mutmaßlichen Precission-Missmatch-Seiteneffekten fügte ich hinzu, umd die DLL vom lokalen Arbeitsverzeichnis zu laden und die Prozedur darin auszuführen: [...]

Aber ich habe nicht verstanden was windows-user tun sollten/könnten.
Kannst Du eine Anleitung für dummies posten?

Ich werds auf LINUX nicht brauchen, aber vielleicht kann man ja auf der internationalen mailinglist den workaround dann posten.


Gruß,
  Harm




Arnold

Hallo Harm,

die Beschreibung für Windows-Anwender, welche aus der Kommandozeile übersetzen:
  • ZIP-Datei (Anhang im 4. Beitrag) herunterladen, und die darin enthaltene Datei »x87mant53.dll« in das Verzeichnis extrahieren, aus dem heraus Lilypond gestartet wird.
  • In der LY-Datei, welche man übersetzen will, den 4-zeiligen SCHEME-Abschnitt hineinkopieren (oberste Ebene, am besten vor allen anderen Anweisungen)
  • Lilypondübersetzung wie gewohnt starten

Bei Frescobaldi-Anwendern (unter Windows natürlcih) weiß ich nicht, was als Arbeitsverzeichnis benutzt wird. So würde ich ausprobieren, die DLL-Datei sowohl in das in der Windows-Startverknüpfung angegebene Arbeitsverzeichnis, als auch in das Verzeichnis, wo die LY-Datei liegt, abzulegen.

Hier nocheinmal die entscheidenden SCHEME-Zeilen:
#(let ((dynlib (dynamic-link "./x87mant53.dll")))
  (ly:progress "\nGoing to set X87 mantissa length via DLL procedure ... ")
  (dynamic-call "X87mantissa53" dynlib)
  (ly:progress "done.\n"))

Wird die DLL-Datei nicht gefunden, dann wird ein allgemeiner SCHEME-Fehler von Lilypond angezeigt. Wenn alles durchlaüft, dann wird die Meldung »Going to set X87 mantissa length via DLL procedure ... done.« im Fortschritts-Protokoll angezeigt.

Man kann natürlich auch die DLL-Datei in ein beliebiges Verzeichnis ablegen, und diesen Pfad dann »absolut« angeben, aber bitte einen Pfad ohne Umlaute (und ohne andere nicht-US-ASCII-Zeichen) verwenden. z. Bsp.:

  • Es wurde ein Verzeichnis »C:\Programme_privat\Lilypond\myIncludes« erzeugt, in welchem eigene LY-Dateien mit Bibliotheks-Routinen-Charakter abgelegt wurden (mit der -I-Option beim Lilypondaufruf wird der Suchpfad für diese Dateien ergänzt). In das gleiche Verzeichnis wird die DLL abgelegt.
  • In der ersten Zeile der SCHEME-Anweisung wird der Pfad zur DLL entsprechend korrigiert, aber statt »\« verwendet man den normalen Schrägstrich »/«, also »#(let ((dynlib (dynamic-link "C:/Programme_privat/Lilypond/myIncludes/x87mant53.dll")))«

Als gobale Festlegung dieser »Anpassung« wäre es dann möglich, diese SCHEME-Zeilen (mit absolutem Pfad) in die Datei ».../ly/init.ly« der Lilypond-Installation zu ergänzen.




Und jetzt noch eine Bitte an Dich, Harm!
Kannst Du nochmal eine verändere Windows-Version erstellen?
Entweder vom aktuellen Develop-Stand oder von der 2.19.83 ausgehend nur die zusätzlichen Zeilen in lily/main.cc wie in meinem vorigen Beitrag angegeben (die Zeilen, bei denen ein Plus-Zeichen vorangestellt ist) hinzufügen.
Ich hoffe, daß sich kein Tippfehler eingeschlichen hat, und ich finde, daß diese Änderung dann so auch in die Produktiv-Version einfließen sollte. Wie beschrieben, wird durch diese Ergänzung (nur) bei einer MINGW-Windows-Compilation, falls _FPU_SETCW() nicht zur Verfügung steht, diese Aktion mit Assembler-Code realisiert.

Arnold

harm6

ZitatUnd jetzt noch eine Bitte an Dich, Harm!
Kannst Du nochmal eine verändere Windows-Version erstellen?
Entweder vom aktuellen Develop-Stand oder von der 2.19.83 ausgehend nur die zusätzlichen Zeilen in lily/main.cc wie in meinem vorigen Beitrag angegeben (die Zeilen, bei denen ein Plus-Zeichen vorangestellt ist) hinzufügen.

Kann ich machen, aber erst nachdem ich aus Salzburg zurück bin und Zeit dafür finde.
Realistisch gesehen also erst in 2 Wochen...

Noch ein paar Fragen: Nachdem lily/main.cc mit Deinem Vorschlag gepatched ist, braucht der windows user die DLL-Datei dann noch oder sollte sie irgendwo mit in den Installer gepackt werden?

Der Code, beginnend:
    #(let ((dynlib (dynamic-link "./x87mant53.dll"))) ...)
könnte ja auch nur bei Abfrage von
    (if (eq? PLATFORM 'windows)
ausgeführt werden.
Ist das sinnvoll oder macht das main.cc dann schon?

Wie Du aus den Fragen ersehen kannst, habe ich die geplanten Abläufe nicht wirklich verstanden ...


Gruß,
  Harm

Arnold

Hallo Harm,

die Zeit ist kein Problem. Eventuell kannst Du dieses »Projekt« ja auch in Salzburg kurz ansprechen - vielleicht nur den folgenden Abschnitt »Hintergrund« weitergeben.

Und, wie Du wohl schon vermutet hast, der Code in main.cc würde das erledigen, was sonst die DLL noch nachträglich erledigen kann.
Dann ist also keine DLL und kein zusätzlicher SCHEME-Code mehr nötig.
Und manche »bugs due to floating point precision truncation on upredictable places« würden auch behoben.



Hintergrund:

Zitat aus main.cc:
x86 defaults to using 80-bit extended precision arithmetic. This can cause problems because the truncation from 80 bits to 64 bits can occur in unpredictable places. To get around this, we tell the x87 FPU to use only double precision. Note that this is not needed for x86_64 because that uses the SSE unit by default instead of the x87 FPU.

Unser LILYPOND-Problem unter Windows: In MINGW steht der zu diesem Zweck verwendete Aufruf _FPU_SETCW (und fpu_control.h) nicht (mehr) zur Verfügung. (Das ist ein Ergebnis der bisherigen Tests)
Ob der Text unter https://600800.xyz/software/gnulib/manual/html_node/fpu_005fcontrol_002eh.html#fpu_005fcontrol_002eh noch aktuell ist?
11.14 fpu_control.h
Handling of the FPU control word. Defines the fpu_control_t type, declares the __fpu_control variable, and defines the _FPU_GETCW, _FPU_SETCW macros.
Portability problems not fixed by Gnulib:
This header file is missing on all non-glibc platforms: Mac OS X 10.5, FreeBSD 6.0, NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 11.4, Cygwin, mingw, MSVC 14, Interix 3.5, BeOS, Android 9.0.


Mein Lösungsansatz für Lilypond:
Wenn diese Include-Datei fehlt, dann (exclusiv für MINGW-32Bit-Übersetzung) diese Aktion in Assembler ausführen.



Arnold

harm6

Zitat von: ArnoldEventuell kannst Du dieses »Projekt« ja auch in Salzburg kurz ansprechen

Mach ich, falls sich die Gelgenheit ergibt.

Gruß,
  Harm

harm6

Hallo Arnold,

ich habe Deinen Code in main.cc eingefügt (auf Basis von 2.19.83):


$ git log -p
commit df6616bae7c82b9e5d20e48d1c868037ce265f43 (HEAD -> arnold-fix-25-1-2020)
Author: Thomas Morley <thomasmorley65@gmail.com>
Date:   Sat Jan 25 10:22:20 2020 +0100

    Arnold fix mantissen-länge

diff --git a/lily/main.cc b/lily/main.cc
index a1cf057f1d..dc71a50539 100644
--- a/lily/main.cc
+++ b/lily/main.cc
@@ -212,6 +212,30 @@ configure_fpu ()
static void
configure_fpu ()
{
+#if ((defined (__x86__) || defined (__i386__)) \
+  && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__))
+  /* If this is a MINGW compilation (for Windows),
+   * but not using SSE2 arithmetic unit nor is a 64 bit compilation (which uses SSE2 by default)
+   * _FPU_SETCW() got lost in the MINGW libraries!
+   * Here is an inline assembler call to execute the same task for the X87 arithmetic unit.
+   *
+   * TODO: output a message what we're going to do here, _only_ if DEBUG level is selected,
+   *       but configure_fpu() is called before the commandline options get evaluated.
+   *       At the moment the info message will be blanked on the console, but if output is
+   *       directed into a file, most editors will show it.
+   */
+  fprintf(stderr, " X87 FPU setup via asm() ... ")
+  asm(
+      "     push %ebp"
+    "\n     mov  $0x027f, %eax"
+    "\n     push %eax"
+    "\n     mov  %esp, %ebp"
+    "\n     fldcw (%ebp)"
+    "\n     pop %eax"
+    "\n     pop %ebp"
+  );
+  fprintf(stderr, "done.\r                                 \r");
+#endif /* (defined(__x86__) || defined(__i386__)) && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__) */
}

#endif /* defined(__x86__) || defined(__i386__) */


Ein lokales make auf meinem Linux-Rechner funktioniert.

Dann habe ich GUB angeworfen. Hier wird aber ein Fehler ausgegeben:
Zitat
/home/hermann/gub/target/mingw/src/lilypond-localhost--lilypond.git-arnold-fix-25-1-2020/lily/main.cc: In function 'void configure_fpu()':
/home/hermann/gub/target/mingw/src/lilypond-localhost--lilypond.git-arnold-fix-25-1-2020/lily/main.cc:228:3: error: expected ';' before 'asm'
   asm(
   ^

Ich weiß nicht woran das liegt, möglicherweise eine andere gcc-Version. GUB verwendet 4.9.4
Kann es auch nicht fixen...

Gruß,
  Harm

Arnold

Hallo Harm,

entschuldige meinen Syntaxfehler, den ich da versehentlich eingebaut habe.
Am Ende des fprintf-Kommandos muß noch ein Semicolon angehängt werden:
fprintf(stderr, " X87 FPU setup via asm() ... ");
Das ist das »;«, welches der Compiler vor dem »asm« erwartet.
Also:
$ git log -p
commit df6616bae7c82b9e5d20e48d1c868037ce265f43 (HEAD -> arnold-fix-25-1-2020)
Author: Thomas Morley <thomasmorley65@gmail.com>
Date:   Sat Jan 25 10:22:20 2020 +0100

    Arnold fix mantissen-länge

diff --git a/lily/main.cc b/lily/main.cc
index a1cf057f1d..dc71a50539 100644
--- a/lily/main.cc
+++ b/lily/main.cc
@@ -212,6 +212,30 @@ configure_fpu ()
static void
configure_fpu ()
{
+#if ((defined (__x86__) || defined (__i386__)) \
+  && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__))
+  /* If this is a MINGW compilation (for Windows),
+   * but not using SSE2 arithmetic unit nor is a 64 bit compilation (which uses SSE2 by default)
+   * _FPU_SETCW() got lost in the MINGW libraries!
+   * Here is an inline assembler call to execute the same task for the X87 arithmetic unit.
+   *
+   * TODO: output a message what we're going to do here, _only_ if DEBUG level is selected,
+   *       but configure_fpu() is called before the commandline options get evaluated.
+   *       At the moment the info message will be blanked on the console, but if output is
+   *       directed into a file, most editors will show it.
+   */
+  fprintf(stderr, " X87 FPU setup via asm() ... ");
+  asm(
+      "     push %ebp"
+    "\n     mov  $0x027f, %eax"
+    "\n     push %eax"
+    "\n     mov  %esp, %ebp"
+    "\n     fldcw (%ebp)"
+    "\n     pop %eax"
+    "\n     pop %ebp"
+  );
+  fprintf(stderr, "done.\r                                 \r");
+#endif /* (defined(__x86__) || defined(__i386__)) && defined (__MINGW32__) && defined (__code_model_32__) && !defined(__SSE2_MATH__) */
}

#endif /* defined(__x86__) || defined(__i386__) */



Einen positiven Nebeneffekt hatte der Fehler allerdings.
Dadurch wurde gezeigt, daß die Präprozessoranweisungen »#if ...« wirklich die Codezeilen bei MINGW-Übersetzung auswerten lassen, bei Linux-Übersetzung aber ausblenden.

Arnold


harm6

Hallo Arnold,

ich habe jetzt einen installer erstellt, der hoffentlich korrekt ist ...
Zwar weiß ich nicht mehr genau, wie wir die Zustellung beim letzten mal gemacht haben, aber ich schick Dir mal einen link.

Gruß,
  Harm

Arnold

Hallo Harm,

alles positiv.
Meine beiden Problem sind behoben, und die Flut an 'mis-predicted force'-Meldungen ist auch gebannt.

Nun könntest Du entscheiden, ob noch andere Window-Anwender diese Kompilation testen sollen, oder ob die Änderung gleich in den zentralen Quellcode einfließen soll.

Arnold

harm6

Hallo Arnold,

sehr schön!!

Ich mach eventuell beides. Aber soweit es einen patch betrifft, für welche issue-Nummer soll ich ihn einreichen?
Issue 4943 ?
Ich würde dann natürlich Dich als Autor benennen und würde Dich um eine patch-description bitten.

Gruß,
  Harm

Arnold

#14
Ja, Harm, mehmen wir Issue 4943.

Wie wäre es mit dieser Beschreibung?
As Issue 4943 on Windows compilation was triggered by missing _FPU_SETCW() in the MINGW libraries,
I added an alternate call to initiate the X87 FPU setup as an inline-assembler command - for MINGW 32 Bit compilation only.


Wenn es zu lang ist, wie ich schon vermute, vielleicht:
Inline assembler fallback for _FPU_SETCW() missing in MINGW libraries.

Arnold