Welche ABI, falls vorhanden, schränkt die Größe ein [u]intmax_t?

Lesezeit: 8 Minuten

Benutzer-Avatar
Keith Thompson

Beginnend mit der Ausgabe von 1999 definiert der ISO-C-Standard einen Standard-Header <stdint.h> die unter anderem die typedefs definiert intmax_t und uintmax_t. Diese bezeichnen jeweils “einen (vorzeichenbehafteten|vorzeichenlosen) Integer-Typ, der in der Lage ist, jeden Wert eines beliebigen (vorzeichenbehafteten|unsigned) Integer-Typs darzustellen”.

Zum Beispiel, wenn, wie es typisch ist, die breitesten vorzeichenbehafteten und vorzeichenlosen Integer-Typen sind long long int und unsigned long long intdie beide typischerweise 64 Bit sind intmax_t und uintmax_t könnte in definiert werden <stdint.h> folgendermaßen:

typedef long long int intmax_t;
typedef unsigned long long int uintmax_t;

Es gibt eine begrenzte Menge vordefinierter vorzeichenbehafteter und vorzeichenloser Integer-Typen, die von signed, unsignedund schlicht char bis zu signed und unsigned long long int.

C99 und C11 erlauben auch das Definieren von Implementierungen erweiterte Integer-Typendie sich von allen Standardtypen unterscheiden und deren Namen implementierungsdefinierte Schlüsselwörter sind.

Sowohl gcc als auch clang unterstützen auf einigen, aber nicht allen Zielen Typen __int128 und unsigned __int128. Diese verhalten sich wie 128-Bit-Ganzzahltypen, sind es aber nicht behandelt wie erweiterte Integer-Typen, und die Dokumentation für beide Compiler gibt an, dass sie keine erweiterten ganzzahligen Typen unterstützen. Denn diese sind es nicht Integer-Typen wie der Standard den Begriff definiert, die typedefs intmax_t und uintmax_t sind für 64-Bit-Typen, nicht für 128-Bit-Typen.

Nichts davon verstößt gegen den C-Standard (Implementierungen müssen keine erweiterten Integer-Typen haben, und sie dürfen beliebige Erweiterungen haben, solange sie keine streng konformen Programme verletzen). Aber es scheint mir, dass es durchaus Sinn machen würde __int128 und unsigned __int128 als erweiterte Integer-Typen behandelt werden, und für intmax_t und uintmax_t 128-Bit-Typen sein.

Die Begründung für nicht Dabei wird die Größe geändert intmax_t und uintmax_t wäre “eine ABI-inkompatible Änderung”.

Das Clang C++-Statusseite heißt es in Fußnote (5):

Für eine Implementierung wie Clang, die keine erweiterten Integer-Typen bereitstellt, sind keine Compileränderungen erforderlich. __int128 wird nicht als erweiterter ganzzahliger Typ behandelt, da sich ändern intmax_t wäre eine ABI-inkompatible Änderung.

(Ja, hier geht es hauptsächlich um C++, aber die Regeln sind dieselben wie für C.)

In einem gcc-Fehlerberichtwird behauptet:

sizeof(intmax_t) wird von verschiedenen LP64-ABIs festgelegt und kann nicht geändert werden

In beiden Fällen wird für diese Behauptung keine Referenz angegeben.

Ein x86_64 ABI-Dokument mit dem Titel „System V Application Binary Interface, AMD64 Architecture Processor Supplement, Draft Version 0.99.6“ nicht erwähnt intmax_t oder uintmax_toder sogar die <stdint.h> Header. Es spezifiziert Größen und Ausrichtungen für die vordefinierten Integer-Typen (in Abbildung 3.1).

Abschließend meine Frage: Ist die Behauptung, dass die Größen von intmax_t und uintmax_t eingeschränkt durch ABI gültig? Wenn ja, welche ABI schreibt eine solche Anforderung vor? (Und übrigens warum?)

(Meiner Meinung nach ist eine solche Anforderung, falls vorhanden, unklug. Sie widerspricht dem Zweck der Erlaubnis des C-Standards, erweiterte Integer-Typen zu definieren, und der beabsichtigten Bedeutung von intmax_t und uintmax_t. Es macht es viel schwieriger, 128-Bit-Ganzzahltypen effektiv auf Systemen zu verwenden, die sie unterstützen, während auf anderen Systemen auf schmalere Typen zurückgegriffen wird.)

Aktualisieren: In N2303 mit dem Titel “intmax t, a way out” schlägt Jens Gustedt vor, die Definitionen von zu optimieren [u]intmax_t um das Hinzufügen erweiterter Integer-Typen zuzulassen, die breiter als sind long long ohne aktualisieren zu müssen [u]intmax_t. Zum Beispiel, intmax_t könnte eine Typdefinition für sein long longaber die Implementierung könnte immer noch Folgendes bereitstellen: __int128 als ein erweiterter ganzzahliger Typ.

Verweise:

  • Es gibt ein Argument, dass 64 Bit für die meisten Anwendungen ausreichen. Haben intmax_t B. ein Typ mit erweiterter Genauigkeit sein, der Carry/Borrow für Add/Sub und noch mehr Komplexität für Mul/Div erfordert, würde zu mehr aufgeblähtem Code als nötig führen. Grundsätzlich sage ich, dass einige Programmierer davon ausgehen können intmax_t nicht breiter zu sein, als das Ziel effizient unterstützen kann. Das ist kein gutes Argumentseit intmax_t ist auf den meisten 32-Bit-Rechnern 64-Bit. Es wäre schön, wenn es eine gäbe int128_taber intmax_t kann nicht auf bestehenden Plattformen geändert werden, ohne die Rückwärtskompatibilität zu brechen.

    – Peter Cordes

    5. November 2015 um 20:15 Uhr

Wie Colonel Thirty Two anmerkt, würde ein Compiler, der diese Änderung einseitig vornimmt, Aufrufe zwischen Kompilierungseinheiten unterbrechen, die durchlaufen werden uintmax_t Parameter oder Rückgabe uintmax_t Werte. Auch wenn die SysV-ABI nicht definiert, wie diese Typen übergeben werden, ist die Pflege ihrer Definitionen aus praktischen Gründen Teil der Konformität mit der Plattform-ABI.

Selbst wenn es dieses ABI-Problem nicht gäbe, könnte ein Compiler diese Änderung nicht einseitig vornehmen, da dies entsprechende Änderungen an der C-Standardbibliothek jeder Zielplattform erfordern würde. Insbesondere würde es mindestens Updates für die erfordern printf und scanf Funktion Familie, imaxabs, imaxdivund strtoimax und strtoumax und ihre Varianten.

  • Sie haben vergessen, den Präprozessor zu erwähnen, mit dem auch gearbeitet werden soll [u]intmax_t.

    – Jens Gustedt

    28. April 2015 um 20:22 Uhr

  • @JensGustedt: Ich wollte definitiv nicht, dass meine Liste der betroffenen Komponenten vollständig ist =)

    – Stefan Kanon

    28. April 2015 um 20:39 Uhr

Wechselnde Typen wie intmax_t und uintmax_t ändert auch die ABI aller Programme, die sie verwenden, da sie sich jetzt auf verschiedene Typen beziehen.

Angenommen, Sie haben Programm A, das eine Funktion in gemeinsam genutzter Bibliothek B mit a verwendet uintmax_t Parameter. Wenn GCC die Definition von ändert uintmax_t und A (aber nicht B) wird dann neu kompiliert uintmax_t in A und uintmax_t in B beziehen sich nun auf zwei verschiedene Arten, die ABI zu brechen.

  • Es ist zu schade, dass C nie einen Standardweg definiert hat, mit dem Prototypen Typen absolut angeben können. Wie viele Millionen Dollar wären eingespart worden, wenn der Prototyp für printf hätte angeben können “konvertiere alle ganzzahligen Werte in N Bits, alle Fließkommawerte in M ​​Bits und alle Zeiger auf void*“? Auf einem System, wo die größte ganze Zahl printf unterstützt wurde 64 Bit, Code, der das Ergebnis der Addition von 123456 zu einer ausgeben möchte int müssten sich keine Gedanken darüber machen, ob das Ergebnis ein wäre int oder longoder die Größen dieser Typen, da beide als 64-Bit übergeben würden.

    – Superkatze

    16. Juli 2015 um 22:36 Uhr


  • Wenn die printf Funktion ist auf 64 Bit beschränkt, dann auch wenn intmax_t zufällig größer ist, das Beste, was passieren könnte, wenn man versucht, einen zu drucken intmax_t wäre dafür, in einen 64-Bit-Wert konvertiert und übergeben zu werden printf; Dadurch könnten Werte, die in 64 Bit passen, unabhängig vom Typ korrekt gedruckt werden intmax_t.

    – Superkatze

    16. Juli 2015 um 22:39 Uhr

  • @supercat Sie wissen, dass C Typen mit fester Größe in spezifiziert stdint.hund dass vararg-Funktionen mehr als nur ganze Zahlen annehmen, sodass das Abschneiden nur ganzer Zahlen einen seltsamen Eckfall hinzufügen würde?

    – Oberst Zweiunddreißig

    17. Juli 2015 um 1:45 Uhr

  • Das stdint.h Typen interagieren auf implementierungsabhängige Weise mit anderen Typen, sowohl hinsichtlich der Heraufstufung als auch der strengen Aliasing-Regeln. Ich erwähnte printf weil dort die Probleme am offensichtlichsten sind. Auf den meisten Systemen wo int und long sind beide 32-Bit, Druck an int32_t kann beides erfordern %d oder %ld; einer wird Recht haben und der andere wird undefiniertes Verhalten hervorrufen. Einige solcher Systeme erfordern das eine und andere das andere, aber ich wäre nicht überrascht, wenn mindestens 10 % des vorhandenen Codes für solche Systeme das falsche verwenden und nur funktionieren, weil die Implementierungen “nett” sind.

    – Superkatze

    17. Juli 2015 um 15:00 Uhr

  • Ich habe für mein Beispiel ganze Zahlen verwendet, weil maxint_t wurde diskutiert; Bei Fließkommatypen ist das Problem viel schwerwiegender. Ich vermute, dass 90% des Codes was nutzt printf mit Typ long double undefiniertes Verhalten aufruft, und die Tatsache, dass solcher Code nur auf Systemen funktioniert, die bestehen long double und double identisch hat viele Compiler dazu veranlasst, zu definieren long double als 64 Bit, auch wenn sie 80-Bit-Typen für Berechnungen verwenden [using extended-precision for calculations is a good thing from both a semantic and efficiency standpoint if and only if it’s available as a data type].

    – Superkatze

    17. Juli 2015 um 15:04 Uhr

Ich denke, der Schlüssel zum Verständnis hier ist, dass etwas, das nicht in einer ABI-Spezifikation dokumentiert ist, nicht bedeutet, dass es nicht Teil einer ABI ist. Sobald ein Typ über eine Bibliotheksgrenze hinweg verwendet wird, werden seine Eigenschaften Teil der ABI dieser Bibliothek.

Indem (u)intmax_t in einem Standardheader definiert und in Funktionen der Standardbibliothek verwendet werden, werden sie Teil der ABI dieser Bibliothek, unabhängig davon, ob sie in einer formalen ABI-Spezifikation enthalten sind oder nicht.

Dies ist insbesondere ein Problem für Unix-ähnliche Plattformen, bei denen die C-Standardbibliothek als Teil der Plattform und nicht als Teil des Compilers behandelt wird.

Nun wäre eine Umstellung möglich. Printf verwendet Makros für Typbezeichner, sodass diese Makros je nach Größe von intmax_t unterschiedlich definiert werden können. Makros könnten auf ähnliche Weise verwendet werden, um die Handvoll Funktionen in der Standardbibliothek verschiedenen Implementierungen zuzuordnen, aber es ist ein Haufen zusätzlicher Arbeit für fragwürdige Gewinne, daher ist es kaum verwunderlich, dass gcc den Weg des geringsten Widerstands gegangen ist, um die benötigte Funktionalität hinzuzufügen.

1334160cookie-checkWelche ABI, falls vorhanden, schränkt die Größe ein [u]intmax_t?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy