2016-07-12 5 views
2

Ich verwende Sun Studio 12.4 und 12.5 auf Solaris 11. Wir haben eine Quelldatei, die eine direkte C/C++ - Implementierung von CRC32 oder eine optimierte Version von CRC32 unter Verwendung von Intel-Intrinsics bietet. Zur Laufzeit wird ein Funktionszeiger mit der richtigen Implementierung gefüllt.Detect -xarch Option im Präprozessor?

Testen auf einem x86-Server mit Dual-Xeon erzeugt das Folgende, weil wir Codepfade basierend auf Compiler-Versionen verfügbar machen. SunCC 12.1 hat Unterstützung für SSE4 hinzugefügt (wenn ich die Matrix richtig analysiert habe), versuchen wir es zu aktivieren, wenn __SUNPRO_CC >= 0x5100.

"crc.cpp", line 311: ube: error: _mm_crc32_u8 intrinsic requires at least -xarch=sse4_2. 

SunCC tut nicht üblichen GCC definiert definieren, wie __SSE4_1__ und __SSE4_2__. Darüber hinaus scheint SunCC keine Intrinsics wie MS VC++ verfügbar zu machen, wo eine Compiler-Version die Unterstützung anzeigt.

SunCC scheint Funktionen basierend auf -xarch Optionen zu aktivieren, aber es ist mir nicht klar, wie man es im Präprozessor erkennt. Darüber hinaus setzt die Verwendung von -xarch einige Bits, die das Programm zum Fehlschlagen führen, auf Downlevel-Prozessoren ausführen (so etwas wie eine "minimale" Plattform).

Ich habe zwei Fragen.

  • Wie erkenne ich die -xarch Option im Präprozessor?
  • Wie deaktiviere ich die -xarch Bits, damit das Programm auf Down-Level-Prozessoren laufen kann?

Unten finden Sie aus einem Makro-Dump mit -xarch=aes kompilieren. Beachten Sie, dass keine verfügbaren Funktionen angezeigt werden.

$ /opt/solarisstudio12.4/bin/CC -native -m64 -xarch=aes -xdumpmacros -E /dev/null 2>&1 | /usr/gnu/bin/sort --ignore-case 

#1 "/dev/null" 
#define __alignof__ __alignof 
#define __amd64 1 
#define __amd64__ 1 
#define __ARRAYNEW 1 
#define __asm asm 
#define __asm__ asm 
#define __attribute __attribute__ 
#define __builtin_constant_p __oracle_builtin_constant_p 
#define __builtin_fpclassify __oracle_builtin_fpclassify 
#define __builtin_huge_val __oracle_builtin_huge_val 
#define __builtin_huge_valf __oracle_builtin_huge_valf 
#define __builtin_huge_vall __oracle_builtin_huge_vall 
#define __builtin_infinity __oracle_builtin_infinity 
#define __builtin_isfinite __oracle_builtin_isfinite 
#define __builtin_isgreater __oracle_builtin_isgreater 
#define __builtin_isgreaterequal __oracle_builtin_isgreaterequal 
#define __builtin_isinf __oracle_builtin_isinf 
#define __builtin_isless __oracle_builtin_isless 
#define __builtin_islessequal __oracle_builtin_islessequal 
#define __builtin_islessgreater __oracle_builtin_islessgreater 
#define __builtin_isnan __oracle_builtin_isnan 
#define __builtin_isnormal __oracle_builtin_isnormal 
#define __builtin_isunordered __oracle_builtin_isunordered 
#define __builtin_nan __oracle_builtin_nan 
#define __builtin_signbit __oracle_builtin_signbit 
#define __BUILTIN_VA_STRUCT 1 
#define __cplusplus 199711L 
#define __DATE__ "Jul 11 2016" 
#define __FILE__ 
#define __has_attribute(x) __oracle_has_attribute(x) 
#define __has_nothrow_assign(x) __oracle_has_nothrow_assign(x) 
#define __has_nothrow_constructor(x) __oracle_has_nothrow_constructor(x) 
#define __has_nothrow_copy(x) __oracle_has_nothrow_copy(x) 
#define __has_trivial_assign(x) __oracle_has_trivial_assign(x) 
#define __has_trivial_constructor(x) __oracle_has_trivial_constructor(x) 
#define __has_trivial_copy(x) __oracle_has_trivial_copy(x) 
#define __has_trivial_destructor(x) __oracle_has_trivial_destructor(x) 
#define __has_virtual_destructor(x) __oracle_has_virtual_destructor(x) 
#define __is_abstract(x) __oracle_is_abstract(x) 
#define __is_base_of(x,y) __oracle_is_base_of(x,y) 
#define __is_class(x) __oracle_is_class(x) 
#define __is_empty(x) __oracle_is_empty(x) 
#define __is_enum(x) __oracle_is_enum(x) 
#define __is_final(x) __oracle_is_final(x) 
#define __is_literal_type(x) __oracle_is_literal_type(x) 
#define __is_pod(x) __oracle_is_pod(x) 
#define __is_polymorphic(x) __oracle_is_polymorphic(x) 
#define __is_standard_layout(x) __oracle_is_standard_layout(x) 
#define __is_trivial(x) __oracle_is_trivial(x) 
#define __is_union(x) __oracle_is_union(x) 
#define __LINE__ 
#define __LP64__ 1 
#define __PRAGMA_REDEFINE_EXTNAME 1 
#define __STDC__ 0 
#define __sun 1 
#define __SUN_PREFETCH 1 
#define __SunOS 1 
#define __SunOS_5_11 1 
#define __SUNPRO_CC 0x5130 
#define __SUNPRO_CC_COMPAT 5 
#define __SVR4 1 
#define __TIME__ "20:58:00" 
#define __underlying_type(x) __oracle_underlying_type(x) 
#define __unix 1 
#define __volatile volatile 
#define __volatile__ volatile 
#define __x86_64 1 
#define __x86_64__ 1 
#define _BOOL 1 
#define _LARGEFILE64_SOURCE 1 
#define _LP64 1 
#define _SIGNEDCHAR_ 1 
#define _TEMPLATE_NO_EXTDEF 1 
#define _WCHAR_T 
#define sun 1 
#define unix 1 

Antwort

3

Für Ihre zweite Frage:

wie kann ich den -xarch Bits deaktivieren, damit das Programm auf Down-Ebene Prozessoren laufen kann?

Siehe Kapitel 7 Capability Die Verarbeitung des Linkers und Bibliotheken Guide:

https://docs.oracle.com/cd/E53394_01/html/E54813/index.html

Dies zeigt Ihnen, wie mehrere Instanzen der gleichen Funktion zu liefern, die mit den Fähigkeitsbits Tags versehen sind. Der Runtime Linker wird auflösen, welche Funktion basierend auf den gemeldeten Fähigkeiten verwendet wird.

Wenn Sie die Fähigkeitsbits wirklich selbst verwalten möchten, siehe Kapitel 9 Mapfiles im Abschnitt Abschnitt "CAPABILITY-Richtlinie". Dies zeigt, wie Funktionen aus dem generierten Objekt entfernt werden.

+0

Dank John. Wir schützen unsere Code-Pfade zur Laufzeit, so dass wir normalerweise nur den Compiler benötigen, um den ASM oder den intrinsischen Code verfügbar zu machen. Das heißt, Build für SSE2, und wir können den Rest wie optimierte SSE3 und SSE4-Implementierungen behandeln. Siehe zum Beispiel [crc.cpp] (http://github.com/weidai11/cryptop/blob/master/crc.cpp) und [blake2.cpp] (http://github.com/weidai11/cryptopp/ Blob/Master/Blake2.cpp). – jww

+0

Ich nehme an, dass die Wächter, auf die Sie verweisen, die Funktionen HasSSE2() und HasSSE4() sind. Wenn diese Laufzeit die Hardwarefähigkeiten überprüft, dann kompilieren Sie einfach mit Studio die Option -Mapfile. mapfile würde CAPABILITY-Einträge wie "HW - = SSE2" enthalten. Maskiere jede Fähigkeit, die benötigt wird, um auf dem System mit der niedrigsten Leistungsfähigkeit zu laufen, das die Fallback-Funktionen bereitstellt. –

+0

Danke John. Ich muss so viel über Solaris lernen. Jahrelang war es ein rothaariges Stiefkind, das nur eine C/C++ - Implementierung hatte. Als Lernübung wollte ich es zu einem First-Class-Bürger machen, damit es alle Geschwindigkeitsvorteile erhält. (Sun Studio 12.1 und höher, das als erstes GCC-Inline-Assembly verwendete). – jww

1

Zuerst möchten Sie die Befehlssatzflags nicht aus Ihren kompilierten Binärdateien entfernen. Wenn Sie mit einer -xarch=NNNN Option kompilieren, wird die Kompilierung diese Anweisungen verwenden. Wenn Sie versuchen, auf einem "niedrigeren" Prozessor zu laufen, der die Anweisungen aus der Architektur, die Sie im Argument -xarch angegeben haben, nicht implementiert, hat Ihre Binärdatei eine gute Chance, nicht zu funktionieren.

Vom Solaris Studio 12.4: C User's Guide:

1,3 Binärkompatibilität Verification

Auf Solaris-Systemen mit Solaris Studio 11 beginnen, Programmbinärdateien mit dem Oracle Solaris Studio Compiler kompiliert werden mit Architektur Hardware gekennzeichnet Flags, die die von angenommenen Befehlssätze anzeigen, die kompilierte Binärdatei. Zur Laufzeit werden diese Marker-Flags auf überprüft, um zu überprüfen, ob die Binärdatei auf der Hardware ausgeführt werden kann, die versucht wird, auf auszuführen.

Programme laufen, die mit den entsprechenden Funktionen oder Befehlssatzerweiterungen nicht über diese Architektur Hardware-Flags auf Plattformen enthalten, die nicht ohne explizite Warnmeldungen auftreten, bei der Segmentierung aktiviert sind Fehler oder falsche Ergebnisse zur Folge haben könnte.

Beachten Sie auch die Erwähnung von Features sowie Anweisungssätze. Nach meiner Erfahrung mit der Solaris-Dokumentation reicht diese eine kleine Warnung aus, dass es wahrscheinlich eine ganze Menge mehr zu

gibt. Ich weiß nicht, wie man den verfügbaren Befehlssatz über den Präprozessor erkennen kann. Sie können möglicherweise Hilfe im Oracle-Forum für Solaris Studio unter https://community.oracle.com/community/server_%26_storage_systems/application_development_in_c__c%2B%2B__and_fortran/developer_studio_c_c%2B%2B_fortran_compilers

bekommen Ich vermute, dass auch dort, finden Sie keine Möglichkeit, den Präprozessor zu verwenden. Die Bereitstellung von plattform- und instruktionssatzspezifischen Implementierungen unter Solaris erfolgt normalerweise über bestimmte gemeinsame Objekte. Von den Solaris Linker and Libraries Guide:

Instruction Set Spezifischen Shared Objects

Die dynamischen Token $ISALIST zur Laufzeit erweitert wird, um die nativen Befehlssätze ausführbare Datei auf dieser Plattform zu reflektieren, wie isalist(1) von das Dienstprogramm angezeigt.

Alle Zeichenfolgennamen, die das Token $ISALIST enthalten, werden effektiv in mehrere Zeichenfolgen dupliziert ( ). Jeder String ist einem der verfügbaren Befehlssätze zugewiesen. Dieses Token ist nur für Filter oder runpath Spezifikationen verfügbar.

...

oder eine Anwendung mit ähnlichen Abhängigkeiten von einer MMX konfigurieren Pentium Pro ausgeführt wird:

$ ldd -ls prog 
..... 
    find object=libbar.so.1; required by ./libfoo.so.1 
    search path=/opt/ISV/lib/$ISALIST (RPATH from file ./libfoo.so.1) 
     trying path=/opt/ISV/lib/pentium_pro+mmx/libbar.so.1 
     trying path=/opt/ISV/lib/pentium_pro/libbar.so.1 
     trying path=/opt/ISV/lib/pentium+mmx/libbar.so.1 
     trying path=/opt/ISV/lib/pentium/libbar.so.1 
     trying path=/opt/ISV/lib/i486/libbar.so.1 
     trying path=/opt/ISV/lib/i386/libbar.so.1 
     trying path=/opt/ISV/lib/i86/libbar.so.1 

Hinweis, wie die Bibliothek Suche beginnt mit der „höchsten“ Befehlssatz spezifischen Bibliothek und bewegt sich zu "niedrigeren" Bibliotheken. Dadurch können mehrere instruktionssatzspezifische gemeinsame Objekte von "am schnellsten spezifisch" bis "am langsamsten generisch" lokalisiert werden. libc.so auf Solaris bietet dies plattformspezifische Versionen von Bibliotheksfunktionen wie memcpy().

2

Ich glaube, dass für Sie besondere Situation (der zweite Teil davon) die einzige einfache Möglichkeit, was Sie wollen, ist: Kompilieren mit explizit festgelegt "-xarch = sse4.2" (dies ermöglicht dem Compiler, SSE4 zu erweitern .2 intrinsics) und entfernen Sie dann die HWCAP-Bits auf Ihre minimale Architektur (dies macht Ihr Programm auf Pre-SSE4.2-Hardware lauffähig).

Zum Abisolieren HWCAP siehe: https://docs.oracle.com/cd/E23823_01/html/816-5165/elfedit-1.html

(Beispiel 2 ein Hardware-Fähigkeit Bit Entfernen)

+0

Kompilieren mit '-xarch = sse4.2' ermöglicht dem Compiler die Verwendung von SSE4.2-Intrinsics. Wenn Sie das SSE4.2-HWCAP-Bit später entfernen, kann dies zu einer Binärdatei führen, die * diese Eigen- schaften verwendet und dann nicht auf SSE4.2-fähiger Hardware ausgeführt werden kann. Sie würden keine 64-Bit-Binärdatei kompilieren, die ELF-Kopfzeile so ändern, dass sie als 32-Bit-Binärdatei gelesen wird, und erwarten, dass sie auf 32-Bit-Hardware ausgeführt wird, oder? –

+0

Die Themen-Starter erwähnten, dass sie einen Run-Time-Guard für die Auswahl der passenden Implementierung haben, abhängig von der HW, auf der die App läuft. Mir ist bewusst, wofür HWCAP im Allgemeinen ist. Aber dieser Fall ist etwas Besonderes. Es könnte der Fall sein, dass der Compiler etwas Nicht-SSE2 für den nicht-intrinsischen Teil des Codes ausstrahlt. Dies muss überprüft werden. P.S. ELF32 und ELF64 sind die Eigenschaften von ELF selbst (Typen, um genauer zu sein) nicht irgendein Wert von HWCAP Abschnitt. Du überspannst dich also hier etwas. – Anton

+0

* Die Topic-Starter erwähnten, dass sie Run-Time-Guard für die Auswahl der passenden Implementierung haben, abhängig von der HW, auf der die App läuft. * Es ist mir nicht klar, dass die mitgelieferten Runtime-Guard-Beispiele garantiert kompilierten Code schützen spezifische Befehlssätze wie SSE2. Aus den angegebenen Beispielen wird ersichtlich, dass Code * außerhalb * der Wächter auch mit der Option kompiliert wird, solche Befehlssätze zu verwenden, so dass das Entfernen der HWCAP-Bits bedeutet, dass Hardware, die keine spezifische Fähigkeit implementiert, immer noch in eine Anweisung läuft, die sie benötigt. –