restrict
« | 13 Mar 2022 | »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 restrict
ed
markieren um zu signalisieren, dass zum Zeitpunkt der Allokierung dieser
Pointer als einziger auf den neuen Speicherblock zeigt.
So lange Pointer ihren restrict
ed 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 restrict
ed 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:
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
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
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.