63 votes

cmm appel format pour les étrangers primop (entier-gmp exemple)

J'ai été vérifier entier-gmp code source pour comprendre comment les étrangers primops peuvent être mises en œuvre en termes de cmm comme documenté sur GHC Primops page. Je suis conscient de techniques à mettre en œuvre en utilisant llvm hack ou fvia-C/gcc - c'est plus une expérience d'apprentissage pour moi de comprendre cette troisième approche qui entier-librairie gmp utilise.

Donc, j'ai regardé CMM tutoriel sur MSFT page (lien pdf), est passé par de GHC CMM page, et il y a encore des questions sans réponse (difficile de garder tous ces concepts dans la tête sans creuser la CMM, qui est ce que je fais maintenant). Il y a ce fragment de code d' entier-bmp cmm fichier:

integer_cmm_int2Integerzh (W_ val)
{
   W_ s, p; /* to avoid aliasing */

   ALLOC_PRIM_N (SIZEOF_StgArrWords + WDS(1), integer_cmm_int2Integerzh, val);

   p = Hp - SIZEOF_StgArrWords;
   SET_HDR(p, stg_ARR_WORDS_info, CCCS);
   StgArrWords_bytes(p) = SIZEOF_W;

   /* mpz_set_si is inlined here, makes things simpler */
   if (%lt(val,0)) {
        s  = -1;
        Hp(0) = -val;
   } else {
     if (%gt(val,0)) {
        s = 1;
        Hp(0) = val;
     } else {
        s = 0;
     }
  }

   /* returns (# size  :: Int#,
                 data  :: ByteArray#
               #)
   */
   return (s,p);
}

Tel que défini dans ghc cmm en-tête:

W_ is alias for word.
ALLOC_PRIM_N is a function for allocating memory on the heap for primitive object.
Sp(n) and Hp(n) are defined as below (comments are mine):
#define WDS(n) ((n)*SIZEOF_W) //WDS(n) calculates n*sizeof(Word)
#define Sp(n)  W_[Sp + WDS(n)]//Sp(n) points to Stackpointer + n word offset?
#define Hp(n)  W_[Hp + WDS(n)]//Hp(n) points to Heap pointer + n word offset?

Je ne comprends pas les lignes 5-9 (ligne 1 est le départ, dans le cas où vous avez 1/0 confusion). Plus précisément:

  • Pourquoi l'appel de la fonction format de ALLOC_PRIM_N (octets,amusant,arg) de cette façon?
  • Pourquoi est p manipulé de cette façon?

La fonction de ce que je comprends (à partir de la recherche à la signature de fonction en Prim.hs) prend un entier et retourne un int, tableau d'octets) (stocké dans s,p respectivement dans le code).

Pour toute personne qui s'interroge sur inline appel en if block, il est de la cmm mise en œuvre des bpf mpz_init_si fonction. Ma conjecture est que si vous appelez une fonction définie dans le fichier de l'objet à travers ccall, il ne peut pas être incorporé (ce qui est logique puisque c'est l'objet du code, pas de code intermédiaire - LLVM approche semble plus adapté pour inline par LLVM IR). Ainsi, l'optimisation a été de définir un cmm de la représentation de la fonction inline. S'il vous plaît corrigez-moi si cette conjecture est fausse.

Explication de lignes 5-9 sera très apprécié. J'ai d'autres questions sur d'autres macros définies en entier-gmp fichier, mais c'est peut être trop demander à un poste. Si vous pouvez répondre à la question avec un Haskell page wiki ou un blog (vous pouvez poster le lien de réponse), ce serait très apprécié (et si vous le faites, je voudrais également apprécier étape-par-étape de plain-pied par le biais d'un entier-gmp cmm macro comme GMP_TAKE2_RET1).

5voto

Reid Barton Points 3218

Ces lignes allouer une nouvelle ByteArray# sur le Haskell tas, donc, pour les comprendre, vous devez d'abord savoir un peu plus sur la façon de GHC du tas est géré.

  • Chaque capacité (= OS thread qui exécute du code Haskell) a son propre crèche, une zone du tas dans lequel il fait normale, petite allocations comme celui-ci. Les objets sont simplement attribués séquentiellement dans cette zone de faible adresses adresses élevées jusqu'à ce que la capacité essaie de faire une allocation qui dépasse l'espace restant dans la pépinière, ce qui déclenche le garbage collector.

  • Tous les tas d'objets sont alignés à un multiple de la taille de mot, c'est à dire, 4 octets sur les systèmes 32 bits et 8 octets sur les systèmes 64 bits.

  • La Cmm niveau du registre Hp points (le début de) le dernier mot qui a été attribué dans la pépinière. HpLim points pour le dernier mot qui peut être attribuée dans la pépinière. (HpLim peut également être mis à 0 par un autre thread pour arrêter le monde, pour le GC, ou d'envoyer un asynchrones exception).

  • https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Storage/HeapObjects a des informations sur la mise en page de l'individu tas d'objets. Notamment chaque objet tas commence avec une info pointeur, qui (entre autres choses) identifie ce genre de tas d'objets qu'il est.

Le Haskell type ByteArray# est mis en œuvre avec le tas d'objet de type ARR_WORDS. Un ARR_WORDS objet est (une info pointeur suivie par) taille (en octets), suivie par des données arbitraires (la charge utile). La charge utile n'est pas interprété par le GC, donc il ne peut pas stocker des pointeurs vers les Haskell tas d'objets, mais il peut stocker quoi que ce soit d'autre. SIZEOF_StgArrWords est la taille de l'en-tête commun à tous les ARR_WORDS tas d'objets, et dans ce cas, la charge utile, c'est juste un seul mot, afin SIZEOF_StgArrWords + WDS(1) est la quantité d'espace dont nous avons besoin pour allouer.

ALLOC_PRIM_N (SIZEOF_StgArrWords + WDS(1), integer_cmm_int2Integerzh, val) s'étend à quelque chose comme

Hp = Hp + (SIZEOF_StgArrWords + WDS(1));
if (Hp > HpLim) {
    HpAlloc = SIZEOF_StgArrWords + WDS(1);
    goto stg_gc_prim_n(integer_cmm_int2Integerzh, val);
}

Première ligne augmente les Hp par le montant à allouer. Deuxième ligne vérifie débordement de tas. La troisième ligne est le montant que nous avons essayé de répartir, de sorte que le GC peut le défaire. La quatrième ligne appelle la GC.

La quatrième ligne est la plus intéressante. Les arguments de la GC comment reprendre le fil une fois que la collecte des ordures est à faire: il doit reinvoke integer_cmm_int2Integerzh avec l'argument val. Le "_n" dans stg_gc_prim_n (et le "_N" dans ALLOC_PRIM_N) signifie que val est un non-argument pointeur (dans ce cas un Int#). Si val ont été un pointeur vers un Haskell tas d'objet, le GC doit savoir qu'il est en direct (afin de ne pas obtenir de la collecte) et de reinvoke notre fonction avec la nouvelle adresse de l'objet. Dans ce cas, on utilisera la _p variante. Il ya aussi des variantes comme _pp pour de multiples pointeur arguments, _d pour Double# arguments, etc.

Après la ligne 5, nous avons attribué un bloc de SIZEOF_StgArrWords + WDS(1) octets et, rappelez-vous, Hp points à son dernier mot. Donc, p = Hp - SIZEOF_StgArrWords ensembles de p au début de ce bloc. Lignes 8 remplit l'info pointeur de p, l'identification de la nouvellement créée, objet tas comme ARR_WORDS. CCC est l'actuel centre de coût pile, utilisé uniquement à des fins de profilage. Lorsque le suivi est activé, chaque tas d'objet contient un champ supplémentaire qui sert essentiellement à identifier qui est responsable de son affectation. En non-profilage construit, il n'y a pas de CCC et SET_HDR juste définit l'info pointeur. Enfin, la ligne 9 se remplit dans le champ taille de l'objet ByteArray#. Le reste de la fonction remplit la charge utile et le retour à la valeur du signe et de l'objet ByteArray# pointeur d'objet.

Donc, cela a fini par être plus sur le GHC tas de sujet de la Cmm, de la langue, mais j'espère que cela aide.

3voto

Margus Points 6175

enter image description here

Connaissances requises

Dans le but de faire de l'arithmétique et logique des opérations des ordinateurs disposent de circuit numérique appelé ALU (Unité Arithmétique et Logique) dans leur CPU (Unité Centrale de Traitement). Un ALU charge les données à partir des registres d'entrée. Processeur registre est la mémoire de stockage dans le cache L1 (demandes de données dans un délai de 3 CPU clock ticks) mis en œuvre en SRAM(Static Random Access Memory) situé dans la puce du PROCESSEUR. Un processeur contient souvent plusieurs types de registres, habituellement différenciés par le nombre de bits qu'ils peuvent tenir.

Les chiffres sont exprimés en discret bits peut contenir nombre fini de valeurs. Généralement, les numéros sont les suivants types primitifs exposés par le langage de programmation (en Haskell):

 8 bit numbers = 256 unique representable values
16 bit numbers = 65 536 unique representable values
32 bit numbers = 4 294 967 296 unique representable values
64 bit numbers = 18 446 744 073 709 551 616 unique representable values

Calcul en précision fixe pour ces types a été mis en œuvre dans le matériel. La taille de mot désigne le nombre de bits qui peuvent être traitées par le PROCESSEUR d'un ordinateur d'un seul coup. Pour x86 l'architecture c'est de 32 bits et x64 c'est 64 bits.

La norme IEEE 754 définit les nombres à virgule flottante standard pour {16, 32, 64, 128} peu de chiffres. Par exemple 32 bits nombre de point (4 294 967 296 valeurs uniques) peut contenir approximative des valeurs de [-3.402823e38 à 3.402823e38] avec la précision d'au moins 7 à virgule flottante chiffres.

enter image description here

En plus

Acronyme BPF signifie GNU Multiple Precision Arithmétique de la Bibliothèque et ajoute le support pour les logiciels émulé en précision arbitraire de l'arithmétique. Glasgow Haskell Compilateur Entier de la mise en œuvre utilise cette.

GMP vise à être plus rapide que n'importe quel autre bignum bibliothèque pour tous opérande tailles. Certains facteurs importants dans cette opération sont:

  • En utilisant les mots que l'arithmétique de base type.
  • En utilisant des algorithmes différents pour différents opérande tailles; les algorithmes les plus rapides pour de très grands nombres sont généralement plus lentes pour les petits les numéros.
  • Hautement optimisé code de langage d'assemblage pour le plus important boucles internes, spécialisés pour les différents processeurs.

Réponse

Pour certains Haskell aurait légèrement difficile à comprendre la syntaxe voici donc la version javascript

var integer_cmm_int2Integerzh = function(word) {
  return WORDSIZE == 32 
    ? goog.math.Integer.fromInt(word))
    : goog.math.Integer.fromBits([word.getLowBits(), word.getHighBits()]);
};

goog est Google Fermeture de la bibliothèque de la classe à utiliser se trouve dans les Mathématiques.Entier. Fonctions :

goog.math.Integer.fromInt = function(value) {
  if (-128 <= value && value < 128) {
    var cachedObj = goog.math.Integer.IntCache_[value];
    if (cachedObj) {
      return cachedObj;
    }
  }

  var obj = new goog.math.Integer([value | 0], value < 0 ? -1 : 0);
  if (-128 <= value && value < 128) {
    goog.math.Integer.IntCache_[value] = obj;
  }
  return obj;
};
goog.math.Integer.fromBits = function(bits) {
  var high = bits[bits.length - 1];
  return new goog.math.Integer(bits, high & (1 << 31) ? -1 : 0);
};

Ce n'est pas tout à fait correcte comme type de retour doit être return (s,p);

  • s est la valeur de
  • p est un signe

Afin de résoudre ce BPF emballage doit être créé. Cela a été fait dans Haskell à compilateur JavaScript projet (lien vers la source).

Lignes 5-9

ALLOC_PRIM_N (SIZEOF_StgArrWords + WDS(1), integer_cmm_int2Integerzh, val);
p = Hp - SIZEOF_StgArrWords;
SET_HDR(p, stg_ARR_WORDS_info, CCCS);
StgArrWords_bytes(p) = SIZEOF_W;

Sont comme suit

  • alloue de l'espace en tant que nouveau mot
  • crée pointeur
  • définissez la valeur du pointeur
  • réglez l'indicateur de taille de type

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X