Comment Microsoft peut-il dire que la taille d’un mot dans WinAPI est de 16 bits?

Je viens de commencer à apprendre le WinAPI. Dans le MSDN, l’explication suivante est fournie pour le type de données WORD.

MOT
Un entier non signé de 16 bits. La plage est comprise entre 0 et 65535 en décimal.
Ce type est déclaré dans WinDef.h comme suit:
typedef unsigned short WORD;

Assez simple, et il correspond aux autres ressources que j’ai utilisées pour apprendre, mais comment peut-il dire définitivement que c’est 16 bits? La page des types de données C sur Wikipedia spécifie

court / court int / signé court / signé court int
Type entier signé court.
Capable de contenir au moins la plage [-32767, +32767]; ainsi, sa taille est d’ au moins 16 bits.

Ainsi, la taille d’un short pourrait très bien être de 32 bits selon le standard C. Mais qui décide quelles tailles de bits vont être utilisées de toute façon? J’ai trouvé une explication pratique ici. Plus précisément, la ligne:

… cela dépend des deux processeurs (plus spécifiquement, ISA, architecture du jeu d’instructions, par exemple x86 et x86-64) et des compilateurs, y compris le modèle de programmation.

Donc, c’est l’ISA, ce qui est logique, je suppose. C’est là que je me perds. En regardant la page Windows de Wikipedia, je vois ceci dans la barre latérale:

Plateformes ARM, IA-32, Itanium, x86-64, DEC Alpha, MIPS, PowerPC

Je ne sais pas vraiment ce que ce sont, mais je pense que ce sont des processeurs, chacun ayant une ISA. Peut-être que Windows prend en charge ces plates-formes car elles sont toutes garanties pour utiliser 16 bits pour un unsigned shortunsigned short ? Cela ne semble pas correct, mais je ne connais pas suffisamment ce sujet pour en savoir plus.

Retour à ma question: Comment se fait-il que l’API Windows puisse typedef unsigned short WORD; et puis dire WORD est un entier non signé de 16 bits lorsque le standard C lui-même ne garantit pas qu’un shortshort est toujours de 16 bits?

En termes simples, un WORD est toujours de 16 bits.

Comme un WORD est toujours de 16 bits, mais qu’un unsigned short ne l’est pas, un WORD n’est pas toujours un unsigned short .

Pour chaque plate-forme prise en charge par le SDK Windows, le fichier d’en-tête Windows contient des macros de style #ifdef pouvant détecter le compilateur et sa plate-forme et associer les types définis par Windows SDK ( WORD , DWORD , etc.) aux types de plate-forme de taille appropriée.

C’est la raison pour laquelle le SDK Windows utilise en réalité des types définis en interne, tels que WORD , plutôt que d’utiliser des types de langage: ils peuvent ainsi s’assurer que leurs définitions sont toujours correctes.

Le kit de développement Windows fourni avec les chaînes d’outils Microsoft est peut-être paresseux, car les chaînes d’outils Microsoft c ++ utilisent toujours des courts-métrages non signés de 16 bits.

Je ne m’attendrais pas à ce que windows.h fourni avec Visual Studio C ++ fonctionne correctement s’il est placé dans GCC, clang etc., car de nombreux détails, y compris le mécanisme d’importation des fichiers dll à l’aide des fichiers .iib dissortingbués par Platform SDK, sont spécifiques à Microsoft. la mise en oeuvre.


Une interprétation différente est que:

Microsoft dit qu’un WORD 16 bits. Si “quelqu’un” veut appeler une API Windows, il doit passer une valeur de 16 bits lorsque l’API définit le champ en tant que MOT. Microsoft peut également affirmer que, pour créer un programme Windows valide, en utilisant les fichiers d’en-tête Windows présents dans leur SDK Windows, l’utilisateur DOIT choisir un compilateur avec un short 16 bits.

La spécification c ++ ne dit pas que les compilateurs doivent implémenter short s en 16 bits – Microsoft a déclaré que le compilateur que vous choisissez pour construire des exécutables Windows doit être.

Il y avait à l’origine une supposition que tout le code destiné à fonctionner sous Windows serait compilé avec le propre compilateur de Microsoft – ou un compilateur entièrement compatible. Et c’est comme ça que ça fonctionnait. Borland C: Match C de Microsoft C. Zortech: Microsoft C. gcc correspondant: pas tellement, donc vous n’avez même pas essayé (sans mentionner qu’il n’y avait pas de runtimes, etc.).

Au fil du temps, ce concept a été codifié et étendu à d’autres systèmes d’exploitation (ou peut-être les autres systèmes d’exploitation), et il est maintenant connu sous le nom d’ABI – Application Binary Interface – pour tous les compilateurs de cette plate-forme. pratique, nécessaire) pour correspondre à l’ABI. Et cela signifie que les attentes doivent correspondre aux tailles des types intégraux (entre autres).

Une question intéressante que vous n’avez pas posée est la suivante: pourquoi les 16 bits sont-ils appelés un mot ? Pourquoi le format 32 bits est-il un mot (double mot) sur nos architectures 32 bits et 64 bits, où la taille de la machine native est “32” ou 64, et non 16? Parce que: 80286.

Dans les en-têtes de Windows, il y a beaucoup de #define qui, sur la base de la plate-forme, peut garantir qu’un mot est de 16 bits, un DWORD de 32, etc. Dans le passé, je sais qu’ils dissortingbuent un SDK correct pour chaque plate-forme. En tout cas rien de magique, juste un mélange de #defines et d’en-têtes appropriés.

La terminologie BYTE=8bits , WORD=16bits et DWORD=32bits (double-mot) provient des mnémoniques d’instructions et de la documentation d’Intel pour le 8086. C’est juste la terminologie et, à ce stade, elle n’implique rien à propos de la taille du “mot machine” ” sur la machine exécutant le code.

Ma conjecture:

Ces noms de type C ont probablement été initialement introduits pour la même raison que C99 normalisé uint8_t , uint16_t et uint32_t . L’idée était probablement de permettre aux implémentations C avec un ABI incompatible (par exemple 16 bits int ou 32 bits short ) de toujours comstackr le code utilisant WinAPI, car l’ABI utilise DWORD plutôt que long ou int dans struct s, et args / return.

Au fur et à mesure que Windows évoluait, suffisamment de code commençait à dépendre de différentes manières de la définition exacte de WORD et de DWORD que MS décidait de standardiser les typedef exacts . Cela diffère de l’idée de C99 uint16_t , où vous ne pouvez pas supposer qu’il s’agit d’un unsigned short .

Comme @supercat le fait remarquer , cela peut être important pour les règles d’alias. Par exemple, si vous modifiez un tableau de unsigned long[] via un DWORD* , il est garanti qu’il fonctionnera comme prévu. Mais si vous modifiez un tableau de unsigned int[] via un DWORD* , le compilateur peut supposer que cela n’affecte pas les valeurs de tableaux déjà présentes dans les registres. Cela est également important pour les chaînes de format printf . (La solution de C99 est constituée de macros de préprocesseur comme PRIu32 .)

Ou peut-être que l’idée était d’utiliser des noms qui correspondent à l’asm , pour s’assurer que personne ne soit confus quant à la largeur des types. Au tout début de Windows, écrire des programmes directement dans asm, au lieu de C, était populaire. WORD / DWORD rend la documentation plus claire pour les personnes écrivant dans asm.

Ou peut-être que l’idée était simplement de fournir un type de largeur fixe pour le code portable . eg #ifdef SUNOS : définissez-le sur un type approprié pour cette plate-forme. C’est tout ce qui est bon pour le moment, comme vous l’avez remarqué:

Comment se fait-il que l’API Windows puisse typer un mot court non signé; et puis dire WORD est un entier non signé de 16 bits lorsque le standard C lui-même ne garantit pas qu’un court-circuit est toujours de 16 bits?

Vous avez raison, documenter le typedef s exact signifie qu’il est impossible d’implémenter correctement les en-têtes WinAPI dans un système utilisant un ABI différent (par exemple, un long 64 bits ou short 32 bits). C’est en partie la raison pour laquelle l’ABI Windows x86-64 fait un long 32 bits. L’ABI x86-64 System V (Linux, OS X, etc.) fait long un type 64 bits.

Cependant, chaque plate-forme nécessite un ABI standard . struct layout, et même l’interprétation des arguments de fonction, exige que tout le code s’accorde sur la taille des types utilisés. Le code d’une version différente du même compilateur C peut interagir, et même d’autres compilateurs qui suivent le même ABI. (Cependant, les ABI C ++ ne sont pas assez stables pour être normalisés. Par exemple, g++ n’a jamais standardisé un ABI, et les nouvelles versions ne respectent pas la compatibilité ABI.)

Rappelez-vous que le standard C ne vous dit que ce que vous pouvez assumer pour chaque implémentation C conforme. La norme C dit également que les entiers signés peuvent être signe / magnitude, complément à deux ou complément à deux. Toute plate-forme spécifique utilisera la représentation que le matériel fait, cependant.

Les plates-formes sont libres de normaliser tout ce que la norme de base C laisse indéfinie ou définie par la mise en œuvre. Par exemple, les implémentations x86 C permettent de créer des pointeurs non alignés et même de les déréférencer. Cela arrive __m128i avec les types de vecteurs __m128i .


Les noms réels choisis associent WinAPI à son inheritance x86 et sont malheureusement source de confusion pour ceux qui ne sont pas familiers avec x86 asm, ou du moins l’inheritance DOS 16 bits de Windows.


Les mnémoniques d’instructions 8086 qui incluent w pour mot et d pour dword étaient couramment utilisées comme configuration pour la division signée idiv .

  • cbw : signe étendre AL (octet) dans AX (mot)
  • cwd : signe étendre AX (word) en DX: AX (dword) , c’est-à-dire copier le bit de signe de ax dans chaque bit de dx .

Ces insns existent toujours et font exactement la même chose en mode 32 bits et 64 bits. (386 et x86-64 ont ajouté des versions étendues, comme vous pouvez le voir dans les extraits de la référence Intel insn set.) Il existe également des instructions de chaîne lodsw , rep movsw , etc.

Outre ces mnémoniques, la taille des opérandes doit être explicitement spécifiée dans certains cas, par exemple
mov dword ptr [mem], -1 , où aucun opérande n’est un registre pouvant impliquer la taille de l’opérande. (Pour voir à quoi ressemble le langage d’assemblage, il suffit de démonter quelque chose, par exemple sur un système Linux, objdump -Mintel -d /bin/ls | less ).

Ainsi, la terminologie est omniprésente dans x86 asm, ce que vous devez bien connaître lors du développement d’un ABI .


Plus d’arrière-plan, d’historique et de schémas de nommage actuels

Rien en deçà de ce point n’a rien à voir avec WinAPI ou la question initiale, mais j’ai trouvé cela intéressant.


Voir aussi le wiki du tag x86 pour les liens vers les PDF officiels d’Intel (et plein d’autres bonnes choses). Cette terminologie est toujours omniprésente dans la documentation et les instructions d’Intel et d’AMD, car elle est totalement dépourvue d’ambiguïté dans un document pour une architecture spécifique qui l’utilise de manière cohérente.

386 étend la taille des registres à cdq et introduit l’instruction cdq : cdq (eax (dword) -> edx: eax (qword)). (Aussi introduit movsx et movzx , pour signer ou étendre sans sans avoir besoin de récupérer les données dans eax premier.) Quoi qu’il en soit, quad-word est 64bits, et a été utilisé même dans pré-386 pour les opérandes de mémoire double précision pour fld qword ptr [mem] / fst qword ptr [mem] .

Intel utilise toujours cette convention b / w / d / q / dq pour le nommage des instructions vectorielles .

Par exemple, le mnémonique pshufd insn ( _mm_shuffle_epi32 C insortingnsèque ) est Packed (integer) Shuffle Dword. psraw est un mot arithmétique emballé à droite. (Les insns vectoriels FP utilisent un suffixe ps (single simple) ou pd (double packé) au lieu du préfixe p .)

Comme les vecteurs deviennent de plus en plus larges, le nommage commence à être stupide: par exemple, _mm_unpacklo_epi64 est l’ _mm_unpacklo_epi64 insortingnsèque de l’ instruction punpcklqdq : punpcklqdq L Quad-mots en Double-Quad. (c.-à-d. entrelacer les moitiés basses de 64 bits en une seule de 128b). Ou movdqu pour Move Double-Quad Unaligned charges / stores (16 octets). Certains assembleurs utilisent o (mot oct) pour déclarer des constantes de 16 octets, mais les mnémoniques et la documentation Intel utilisent toujours dq .

Heureusement pour notre bon sens, les instructions AVX 256b (32B) utilisent toujours les mnémoniques SSE, alors vmovdqu ymm0, [rsi] est une charge 32B, mais il n’ya pas de terminologie quad-quad. Les désassembleurs qui incluent des tailles d’opérandes même quand elles ne sont pas ambiguës imprimeraient vmovdqu ymm0, ymmword ptr [rsi] .


Même les noms de certaines extensions AVX-512 utilisent la terminologie b / w / d / q. AVX-512F (fondation) n’inclut pas toutes les versions d’éléments de chaque instruction. Les versions de taille d’élément 8 et 16 bits de certaines instructions sont uniquement disponibles sur le matériel prenant en charge l’ extension AVX-512BW . Il y a aussi AVX-512DQ pour les instructions supplémentaires de taille d’élément dword et qword, y compris la conversion entre des entiers flottants / doubles et 64 bits et une multiplication avec une taille d’élément de 64b x 64b => 64b .


Quelques nouvelles instructions utilisent des tailles numériques dans le mnémonique

Les vinsertf128 d’AVX et similaires pour l’extraction de la voie 128 bits haute d’un vecteur 256 bits auraient pu utiliser dq , mais utiliser 128 .

AVX-512 introduit quelques mnémoniques avec des noms tels que vmovdqa64 (chargement vectoriel avec masquage à la granularité des éléments 64 bits) ou vshuff32x4 (éléments shuffle 128b, avec masquage à la granularité des éléments 32 bits).

Notez que comme AVX-512 a un masquage de fusion ou un masquage zéro pour presque toutes les instructions, même les instructions qui ne concernaient pas la taille des éléments (comme pxor / _mm_xor_si128 ) sont maintenant de différentes tailles: _mm512_mask_xor_epi64 ( vpxorq ) bit affecte un élément 64 bits) ou _mm512_mask_xor_epi32 ( vpxord ). Le _mm512_xor_si512 insortingnsèque sans masque pourrait se comstackr en vpxorq ou vpxord ; ce n’est pas grave

La plupart des nouvelles instructions de l’AVX512 utilisent encore b / w / d / q dans leurs mnémoniques, comme VPERMT2D (éléments de sélection à permutation totale provenant de deux vecteurs sources).

Actuellement, aucune plate-forme ne prend en charge l’API Windows, mais les versions unsigned short sont 16 bits.

Si quelqu’un a déjà créé une telle plate-forme, les en-têtes d’API Windows pour cette plate-forme n’incluront pas la ligne typedef unsigned short WORD; .

Vous pouvez considérer les pages MSDN comme décrivant le comportement typique de MSVC ++ sur les plates-formes x86 / x64.

L’inheritance des types tels que WORD est antérieur à Windows et aux jours de MSDOS suivant les types définis par MASM (plus tard, le nom a été changé en ML). Les types signés de MASM, tels que SBYTE, SWORD, SDWORD, SQWORD, ne sont pas adoptés par l’API Windows.

QWORD / SQWORD dans MASM n’a probablement pas été défini tant que MASM / ML n’a pas pris en charge 80386.

Une référence actuelle:

http://msdn.microsoft.com/en-us/library/8t163bt0.aspx

Windows a ajouté des types tels que HANDLE, WCHAR, TCHAR, ….

Pour les compilateurs Windows / Microsoft, size_t est un entier non signé de la même taille qu’un poitner, 32 bits en mode 32 bits, 64 bits en mode 64 bits.

Les directives de données DB et DW dans MASM remontent à l’époque de l’assembleur Intel 8080.