restrict

Mit der Fehlermeldung

error C2485: ‘constant’: unrecognized extended attribute

eröffnete mir der MSVC 2022 beim Kompilieren von libreSSL die Möglichkeit, wieder einmal über ein “neueres” C Schlüsselwort nachzudenken, nämlich restrict


Was ist ein mit restrict markierter Pointer?

Das C99 Schlüsselwort restrict ist vielen Entwicklern unbekannt und hilft daher eher im Hintergrund, den generierten Code zu optimieren.
restrict “qualifiziert” einen Pointer als alleinigen Zugang zu einer Ressource. Ähnlich wie register ist restrict nur ein Hinweis für den Compiler, der angewendet oder ignoriert werden kann.

1int* restrict foo(int arg1, int* restrict arg2);

z.B.: kann malloc() seine zurückgegebenen Pointer als restricted markieren um zu signalisieren, dass zum Zeitpunkt der Allokierung dieser Pointer als einziger auf den neuen Speicherblock zeigt.

So lange Pointer ihren restricted Status behalten, können Pointeroperationen wie z.B.: memcpy() oder memmove() auf Prüfungen verzichten, ob Quelle und Ziel einer Speicheroperation sich überschneiden.
Zwei per malloc() erzeugte Pointer können per memcpy/move() sofort abgearbeitet werden, und zwar mit den schnellst möglichen Instruktionen, wie z.B. Vektoroperationen.

Nicht restricted Pointer könnten auf sich überlappende Speicherbereiche zeigen, womit memcpy/move() diesen Fall prüfen müssen und erst zur Laufzeit die geeigneten Bearbeitungsinstruktionen auswählen können.
Anders gesagt: Da findet oft noch ein zusätzliches if statt.

Compiler Support

Tja, leider hat jeder Compiler-Hersteller “anders” auf restrict reagiert. Microsoft hat C99 bis MSVC 13 ignoriert, aber schon vorher __declspec(restrict) in C und C++ aufgenommen.

Im GCC hingegen nimmt __restrict__ diesen Platz ein.

Und aus diesem Grund nutzen manche Bibliotheken eigene Makros um nicht restrict sondern die native Variante des Compiler zu nutzen.

Und das wiederum kann die Quelle neuer Fehler sein:

libReSSL und restrict

Als ich den Fehler error C2485 sah, konnte ich ihn zu folgender Codestelle in corecrt.h zurückverfolgen:

1#if defined _CRT_SUPPRESS_RESTRICT || defined _CORECRT_BUILD
2    #define _CRTRESTRICT
3#else
4    #define _CRTRESTRICT __declspec(restrict)
5#endif

Viele allokierende C-Funktionen wie malloc oder calloc werden mit _CRTRESTRICT deklariert.

Die “schnelle” Lösung ist somit, das Makro _CRT_SUPPRESS_RESTRICT zu aktivieren, was in CMake so aussieht:

1add_definitions(/D_CRT_SUPPRESS_RESTRICT)

und tatsächlich würde das mein Problem bereits lösen.

Doch bei genauerer Betrachtung, stellt sich heraus, dass nur libreSSL ein Problem mit der C-Runtime hat, während alle anderen Libs malloc mit _CRTRESTRICT == __declspec(restrict) problemlos nutzen können.

Der Grund liegt in libressl/CMakeLists.txt, denn dort findet man die Zeilen

1if(WIN32)
2  add_definitions(-Drestrict)
3  ...
4endif()

Es wird also ein Makro namens restrict angelegt und ohne weiteren Parameter erhält das Makro den Wert 1.
Das führt dazu, dass _CRTRESTRICT als __declspec(1) aufgelöst wird und schon haben wir die Ursache für

error C2485: ‘constant’: unrecognized extended attribute

gefunden.

Lösung

Ich habe den CMAKE Code von libressl einfach auf

1if(WIN32)
2  if(NOT MSVC)
3    add_definitions(-Drestrict)
4  endif()
5  ...
6endif()

umgestellt, womit MSVC Builds das Makro überspringen. In anderen Windows-Kompilaten wie z.B.: beim MinGW kann das Makro gerne weiter seinen Zweck erfüllen, doch im MSVC stört es.

Tatsächlich stört das Makro nur im Zusammenhang mit den neueren MSVC Compilern und mit der Universal-CRT (UCRT).

Fazit

Im GATE Projekt mache ich weiter einen Bogen um restrict. Mir ist es lieber, wenn in machen Fällen ein nicht-perfekter Code generiert wird, doch dass dieser zumindest überall kompiliert werden kann.

Würde ich restrict selbst benutzen, müsste ich mir genau überlegen, auf welche Plattform es verfügbar ist, und dort, wo es fehlt, ebenfalls Ersatzmakros setzen.

Genau diese “Ersatzmakros” machen dann immer Probleme, wenn unterschiedliche Bibliotheken zusammengeführt werden, und deshalb lasse ich es.

Aber grundsätzlich ist das sehr Schade, dass ein 20 Jahre altes Feature bis heute nicht einheitlich und problemfrei umgesetzt ist und deshalb Probleme bereitet.

Zum Glück können heutige Compiler aber viele Informationen aus dem Code ableiten und auch ohne restrict viele Optimierungen einleiten.

Nachtrag

Und zwei Tage später ist alles anders …

Am 15. März wurde libreSSL 3.4.3 veröffentlicht, wo die -Drestrict Zeile bereits entfernt wurde. Offenbar hat man die sowieso nicht gebraucht.

Das heißt also: Die SSL Jungs haben brav auch ihre Aufgaben gemacht.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!