Un ->
de l'opérateur sur un Pair *
implique qu'il y a tout un Pair
objet entièrement alloué. (@Hurkyl cite la norme.)
x86 (comme toute architecture normale) n'ont pas d'effets secondaires pour accéder à la normale de la mémoire allouée, alors x86 mémoire sémantique sont compatibles avec le C machine abstraite de la sémantique pour les non-volatile
de la mémoire. Les compilateurs peuvent éventuellement en charge si/quand ils pensent que ce sera un rendement à la victoire sur la cible de la microarchitecture ils sont de réglage pour dans une situation donnée.
Notez que sur x86 de protection de la mémoire fonctionne avec la page de granularité. Le compilateur pourrait se dérouler d'une boucle ou d'vectoriser avec SIMD d'une manière qui lit à l'extérieur d'un objet, aussi longtemps que toutes les pages touché contiennent certains des octets de l'objet. Est-il sécuritaire de le lire après la fin d'un tampon à l'intérieur de la même page sur x86 et x64?. libc strlen()
des implémentations écrite à la main à l'assemblée de le faire, mais autant que je sache, gcc n'a pas, au lieu d'utiliser scalaire boucles pour les restes des éléments à la fin d'une auto-vectorisé en boucle même où elle a déjà aligné les pointeurs avec un (entièrement déroulé) démarrage de la boucle. (Peut-être parce qu'il ferait d'exécution vérification de limites avec valgrind
difficile.)
Pour obtenir le comportement que vous attendiez, utiliser un const int *
arg.
Un tableau est un objet unique, mais les pointeurs sont différents tableaux. (Même avec inline dans un contexte où les deux éléments du tableau sont connus pour être accessible, je n'ai pas pu obtenir de gcc pour émettre un code comme il le fait pour la structure, donc si c'est struct code est une victoire, c'est un raté l'optimisation de ne pas le faire sur les tableaux quand il est aussi sûr.).
En C, vous êtes autorisé à passer à cette fonction un pointeur vers un seul int
, tant que c
est non-nul. Lors de la compilation pour les architectures x86, gcc a supposer qu'il pourrait être pointer à la dernière int
dans une page, la page suivante non cartographiées.
Source + gcc et clang sortie pour cela et d'autres variations sur le Godbolt compilateur explorer
// exactly equivalent to const int p[2]
int load_pointer(const int *p, int c) {
int x;
if (c)
x = p[0];
else
x = p[1]; // gcc missed optimization: still does an add with c known to be zero
return c + x;
}
load_pointer: # gcc7.2 -O3
test esi, esi
jne .L9
mov eax, DWORD PTR [rdi+4]
add eax, esi # missed optimization: esi=0 here so this is a no-op
ret
.L9:
mov eax, DWORD PTR [rdi]
add eax, esi
ret
En C, vous pouvez passer sorte de passer un tableau d'objet (par référence) à une fonction, garantissant à la fonction qu'il est permis de toucher à tous, la mémoire, même si le C machine abstraite qui ne fonctionne pas. La syntaxe est - int p[static 2]
int load_array(const int p[static 2], int c) {
... // same body
}
Mais gcc ne pas prendre parti, et émet un code identique à load_pointer.
Hors sujet: clang compile toutes les versions (struct et tableau) de la même manière, à l'aide d'un cmov
de branchlessly calculer une adresse de chargement.
lea rax, [rdi + 4]
test esi, esi
cmovne rax, rdi
add esi, dword ptr [rax]
mov eax, esi # missed optimization: mov on the critical path
ret
Ce n'est pas nécessairement une bonne chose: il a une latence plus élevée que gcc de la structure du code, parce que l'adresse de chargement dépend d'un couple supplémentaire ALU uop. Il est assez bon si les deux adresses ne sont pas sûrs à lire et une branche de prévoir le mal.
Nous pouvons obtenir un meilleur code pour la même stratégie de gcc et clang, à l'aide de setcc
(1 uop avec 1c latence sur tous les Processeurs à l'exception de quelques très anciennes), au lieu de cmovcc
(2 uop sur Intel avant de Skylake). xor
-réduction à zéro est toujours moins cher qu'un LEA, trop.
int load_pointer_v3(const int *p, int c) {
int offset = (c==0);
int x = p[offset];
return c + x;
}
xor eax, eax
test esi, esi
sete al
add esi, dword ptr [rdi + 4*rax]
mov eax, esi
ret
gcc et clang les deux mis la dernière mov
sur le chemin critique. Et sur Intel Sandybridge-famille, de l'indexation mode d'adressage ne reste pas de micro-fusion avec l' add
. Donc, ce serait mieux, à l'instar de ce qu'il fait dans la ramification version:
xor eax, eax
test esi, esi
sete al
mov eax, dword ptr [rdi + 4*rax]
add eax, esi
ret
De simples modes d'adressage, comme [rdi]
ou [rdi+4]
ont 1c latence plus faible que les autres sur Intel banque nationale, de la famille des Processeurs, donc cela pourrait effectivement être pire temps de latence sur Skylake (où cmov
n'est pas cher). L' test
et lea
peuvent s'exécuter en parallèle.
Après l'in-lining, que final mov
n'existe pas, et il pourrait juste add
en esi
.