4 Stimmen

Ersetzen von Array-Zugriffsvariablen durch den richtigen Ganzzahlentyp

Ich hatte die Angewohnheit, int zu verwenden, um auf Arrays zuzugreifen (besonders in for-Schleifen); allerdings habe ich kürzlich entdeckt, dass ich möglicherweise "alles falsch gemacht" habe und mein x86-System die Wahrheit vor mir verborgen hat. Es stellt sich heraus, dass int in Ordnung ist, wenn sizeof(size_t) == sizeof(int) ist, aber wenn es auf einem System verwendet wird, auf dem sizeof(size_t) > sizeof(int) ist, führt es zu einer zusätzlichen mov-Instruktion. size_t und ptrdiff_t scheinen der optimale Weg auf den von mir getesteten Systemen zu sein, da sie keiner zusätzlichen mov-Instruktion bedürfen.

Hier ist ein verkürztes Beispiel

int vector_get(int *v,int i){ return v[i]; }

    > movslq    %esi, %rsi
    > movl  (%rdi,%rsi,4), %eax
    > ret

int vector_get(int *v,size_t i){ return v[i]; }

    > movl  (%rdi,%rsi,4), %eax
    > ret

OK, ich habe mich selbst repariert (verwende jetzt size_t und ptrdiff_t), aber wie finde ich diese Stellen in meinem Code (hoffentlich nicht manuell), damit ich sie reparieren kann?

Kürzlich habe ich festgestellt, dass mehrere Patches mit Änderungen von int zu size_t über den Draht gegangen sind, die Clang erwähnen.


Ich habe eine Tabelle der zusätzlichen Instruktionen zusammengestellt, die in jeder Instanz eingefügt werden, um die Ergebnisse von "alles falsch gemacht" zu zeigen.

         char<br>        short<br>             int<br>unsigned<br>         char<br>unsigned<br>        short<br>unsigned<br>            int movsbq %sil, %rsi<br>movswq %si, %rsi<br>movslq %esi, %rsi<br><br>movzbl %sil, %esi  <br><br>movzwl %si, %esi  <br><br>movl %esi, %esi    <br><br>Tabelle der unerwünschten Bewegungsoperationen beim<br>Zugriff auf Vektoren mit "falschem" Typ.

Hinweis: long, long long, unsigned long, unsigned long long, size_t und ptrdiff_t benötigen keine zusätzliche mov*-Operation (im Grunde alles >= Größe des größten Objekts oder 8 Bytes auf dem 64-Bit-Referenzsystem)

Bearbeitet:

Ich glaube, ich habe einen praktikablen Stub zum Patchen von gcc erstellt, aber ich kenne mich nicht im Quellcode aus, um den Stub zu vervollständigen und ordnungsgemäße -Wflag-Bits hinzuzufügen, und wie üblich ist der schwierigste Teil des Programmierens das Benennen von Dingen. -Wunalinged-index?

gcc/c/c-typeck.c <strong>___</strong>

if (!swapped)
    warn_array_subscript_with_type_char (index);
> 
> if ( sizeof(index) < sizeof(size_t) ) 
>   warning_at (loc, OPT_Wunaligned_index,
>       "array index is smaller than size_t");

/* Apply default promotions *after* noticing character types.  */
index = default_conversion (index);

gcc/c-family/c.opt <strong>_____</strong>

trigraphs
C ObjC C++ ObjC++
-trigraphs  Support ISO C trigraphs
> 
> Wunaligned-index
> C ObjC C++ ObjC++
> Warnen bei Arrayindizes kleiner als size_t 

undef
C ObjC C++ ObjC++ Var(flag_undef)
Systemspezifische und GCC-spezifische Makros nicht vordefinieren

gcc/c-family/c-opts.c <strong>__</strong>

case OPT_Wtrigraphs:
  cpp_opts->warn_trigraphs = value;
  break;
>
> case OPT_Wunaligned_index:
>   cpp_opts->warn_unaligned_index = value;
>

case OPT_Wundef:
  cpp_opts->warn_undef = value;
  break;

1voto

ecatmur Punkte 145884

Clang und gcc haben -Wchar-subscripts, das aber nur helfen wird, char Subskripttypen zu erkennen.

Sie könnten in Betracht ziehen, clang oder gcc zu modifizieren (je nachdem, welches einfacher auf Ihrer Infrastruktur zu erstellen ist), um die von der -Wchar-subscripts Warnung erkannten Typen zu erweitern. Wenn dies ein Einmalaufwand ist, könnte dies der einfachste Weg sein, dies zu bewältigen.

Andernfalls müssen Sie einen Linter finden, der sich über nicht-size_t/ptrdiff_t Subskriptierung beschwert; mir ist keiner bekannt, der diese Option hat.

0voto

Drew McGowen Punkte 11186

Die movslq Anweisung erweitert ein long (auch bekannt als 4-Byte-Menge) zu einem quad (auch bekannt als 8-Byte-Menge). Dies liegt daran, dass int vorzeichenbehaftet ist, sodass ein Offset z.B. -1 als 0xffffffff als long dargestellt wird. Wenn Sie das einfach nur nullerweitern würden (d.h. kein movslq verwenden), würde dies als 0x00000000ffffffff, auch bekannt als 4294967295, was wahrscheinlich nicht das ist, was Sie möchten. Daher erweitert der Compiler stattdessen den Index vorzeichenbehaftet , um 0xffff... zu erhalten, auch bekannt als -1.

Der Grund, warum die anderen Typen nicht die zusätzliche Operation benötigen, liegt darin, dass sie trotz einiger Vorzeichenbehaftung immer noch die gleiche Größe von 8 Byte haben. Und dank des Zweierkomplements kann 0xffff... sowohl als -1 als auch als 18446744073709551615 interpretiert werden, und die 64-Bit-Summe bleibt dennoch gleich.

Nun, normalerweise, wenn Sie stattdessen unsigned int verwenden würden, müsste der Compiler normalerweise eine Null-Erweiterung einfügen, nur um sicherzustellen, dass der obere Teil des Registers nicht Müll enthält. Auf der x64-Plattform wird dies jedoch implizit ausgeführt; eine Anweisung wie mov %eax,%esi wird die 4-Byte-Menge in eax in die unteren 4 Bytes von rsi verschieben und die oberen 4 löschen, wodurch die Menge effektiv null-erweitert wird. Aber aufgrund Ihrer Beiträge scheint der Compiler trotzdem eine mov %esi,%esi Anweisung einzufügen, "um sicher zu gehen".

Beachten Sie jedoch, dass diese "automatische Null-Erweiterung" nicht für 1- und 2-Byte-Mengen gilt - diese müssen manuell null-erweitert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X