Comment les lignes de code sont-elles exécutées par le CPU?

11

J'essaie de vraiment comprendre comment exactement un langage de haut niveau est converti en code machine, puis exécuté par le processeur.

Je comprends que le code est compilé en code machine, qui est le code de bas niveau qu'un processeur peut utiliser. Si j'ai une déclaration de mission, dites:

x = x + 5;
y = x - 3;

Le CPU exécute-t-il chaque ligne une par une? Il exécutera donc d'abord x = x + 5; puis l'instruction suivante que la CPU exécutera est la y = x-3; J'essaie vraiment de comprendre le processus d'exécution et comment le code que j'écris est réellement exécuté par le CPU.

Frankie
la source
Vous voudrez peut-être essayer de comprendre la conception de l'un des CPU open source, il existe des implémentations basées sur la pile très simples comme excamera.com/sphinx/fpga-j1.html - elles sont beaucoup plus simples que les architectures à 3 adresses comme dans votre exemple.
SK-logic
3
Quand je suis entré dans cette entreprise, cela aurait eu des réponses simples et bien définies. De nos jours, les processeurs sont extrêmement compliqués et font toutes sortes de choses pour augmenter la puissance de traitement.
David Thornley

Réponses:

12

Les lignes de code n'ont rien à voir avec la façon dont le CPU l'exécute. Je recommanderais de lire l'assembleur, car cela vous apprendra beaucoup sur la façon dont le matériel fait les choses. Vous pouvez également obtenir la sortie de l'assembleur à partir de nombreux compilateurs.

Ce code peut être compilé en quelque chose comme (dans un langage assembleur composé):

load R1, [x] ; meaning load the data stored at memory location x into register 1
add R1, 5
store [x], R1 ; store the modified value into the memory location x
sub R1, 3
store R1, [y]

Toutefois, si le compilateur sait qu'une variable n'est pas utilisée à nouveau, l'opération de stockage peut ne pas être émise.

Maintenant, pour que le débogueur sache quel code machine correspond à une ligne de source de programme, des annotations sont ajoutées par le compilateur pour montrer quelle ligne correspond à où dans le code machine.

maxpolun
la source
Pourquoi pas? Une architecture à 3 adresses aura des instructions comme ADD Rx, Rx, $5et SUB Ry, Rx, $3(en supposant que les variables x et y ont été mappées dans des registres). Vous décrivez une approche RISC de chargement / stockage.
SK-logic
1
@ SK-logic: Bien que cela puisse arriver pour des lignes de code très simples dans des langages de programmation très simples avec des types de données et des opérations que le CPU supporte assez bien, ce n'est nulle part le cas général. C'est pratique pour les experts, mais d'abord, il est important de réaliser que les instructions de code machine ne résistent généralement guère aux lignes de code dans une langue de haut niveau.
@ SK-Logic: cela ne fonctionne que pour cet exemple particulier. En général, cependant, maxpolun a raison. Les déclarations de langage de haut niveau doivent être traduites dans un langage de niveau inférieur, avec plus de «paperasserie» nécessaire pour faire des choses conceptuellement simples. Je suppose que le PO demandait un exemple de cette transformation.
Andres F.
1
@ SK-Logic: l'OP a commencé sa question par "J'essaie de vraiment comprendre comment exactement une langue de haut niveau [...]"
Andres F.
1
@ SK-logic Le contexte est "Si j'ai une instruction d'affectation, dites: [extrait de code] La CPU exécute-t-elle chaque ligne une par une?" - me semble être destiné à être du code source dans un langage non assembleur. Plus généralement, je ne vois aucun indicateur d'une compréhension de la façon dont le code machine de bas niveau est, et certains phrasés (comme parler de lignes) indiquent certaines idées fausses. Ce n'est pas aussi impossible que vous l'imaginez, tout le monde n'a pas eu le plaisir d'être lancé la tête la première sur de simples microcontrôleurs (comme moi et apparemment d'autres). Frankie devrait peut-être clarifier.
2

Ça dépend.

Au début des machines vraiment simples, oui, le code s'exécutait une ligne à la fois. Comme les machines sont devenues plus grandes, plus rapides et plus complexes, vous avez commencé à voir à la fois la possibilité d'exécuter plusieurs instructions simultanément et les lectures et écritures en mémoire prenant beaucoup plus de temps que les opérations sur les registres.

L'optimisation des compilateurs devait en tenir compte, et les lignes que vous donniez pouvaient être exécutées "plus ou moins" en parallèle, avec une partie du processeur travaillant sur le calcul de y, tandis qu'une autre partie stockait la nouvelle valeur précédemment calculée de x (et le calcul de y utilisait cette nouvelle valeur du registre).

Le Control Data 6600 a été la première machine que je connaisse à faire ce genre de choses. L'ajout d'entiers a pris 300 nsec, la référence de mémoire (lecture ou écriture) a pris 1000 nsec, les multiplications et les divisions ont pris beaucoup plus de temps. Jusqu'à une dizaine d'instructions pourraient toutes être exécutées en parallèle, selon les unités fonctionnelles requises. Les compilateurs CDC 6600 FORTRAN étaient TRÈS bons pour planifier tout cela.

John R. Strohm
la source
Dans ce cas, l'entrée de l'instruction suivante dépend du premier résultat de l'instruction, elle doit donc être exécutée séquentiellement.
SK-logic
@ SK-logic: Pas tout à fait. L'entrée de la deuxième ligne dépend du résultat du côté droit de la première ligne, mais, basé uniquement sur ce que nous pouvons voir dans l'exemple de code d'origine, il ne peut PAS dépendre du stockage en mémoire du résultat de la première ligne. Si x avait été déclaré volatile (en C / C ++), alors le compilateur serait obligé de stocker le résultat en premier, puis de le recharger DE LA MÉMOIRE, avant de commencer à calculer la nouvelle valeur de y, car le "volatile" signifie que quelque chose (un gestionnaire d'interruption, par exemple) pourrait entrer et zapper x entre les deux lignes.
John R. Strohm
J'ai supposé que x et y sont des registres (et le code est dans un langage de pseudo-assemblage à 3 adresses plutôt que quelque chose comme C). Dans ce cas, les deux instructions sont inévitablement séquentielles. Sinon, OP devait poser deux ou plusieurs questions différentes au lieu de celle-ci.
SK-logic
Je me demande si les transformateurs essaieraient de «spéculer» sur la valeur de x? De cette façon, il a déjà exécuté le code et l'a stocké dans le cache.
Kolob Canyon
Même s'il s'agit de registres, EN FONCTION DE LA MACHINE, vous ne pouvez pas supposer que les instructions s'exécutent de manière complètement séquentielle. Le 6600 avait une logique d'ordonnancement (le «tableau de bord») qui forcerait la sémantique séquentielle, basée sur l'hypothèse que le programmeur voulait faire l'évidence. Les machines ultérieures ont omis ce matériel, s'appuyant plutôt sur les compilateurs pour planifier soigneusement les instructions. Les programmeurs humains faisant de la programmation en langage assembleur sur ces bêtes étaient SUR LEUR PROPRE.
John R. Strohm
1

Non, il n'y a pas de mappage un à un entre les lignes de code / instructions dans les langues de niveau supérieur et inférieur. En fait, les deux lignes ci-dessus sont traduites en plusieurs instructions de code machine , comme

  1. charger une valeur d'une certaine adresse mémoire dans un registre
  2. modifier la valeur
  3. réécrire dans la mémoire

Les détails réels de ces instructions varient selon les plateformes.

C'est la vision de base des choses. Cependant, pour compliquer davantage les problèmes, les processeurs modernes appliquent des techniques telles que les pipelines d'exécution , l' exécution dans le désordre et plusieurs cœurs , entre autres. Il en résulte que la CPU fait plusieurs choses à la fois, par exemple les pipelines traitent différentes phases des instructions suivantes en parallèle au sein de la même unité de traitement, tandis que plusieurs cœurs peuvent traiter des instructions indépendantes en parallèle.

Péter Török
la source
0

Vous devriez regarder dans les moindres détails dans un livre pour trouver plus de détails sur son fonctionnement, peut-être aussi une classe de compilation.

Fondamentalement, votre question se concentre sur 2 aspects différents.

1) Comment le code est-il traduit en code machine?

2) Quand / comment le code est-il calculé en utilisant la parallélisation?

La réponse à 1) dépend de la langue que vous utilisez (bien que votre exemple soit trivial, la sortie serait la même). La façon dont le compilateur effectue la traduction en code machine est l'une des forces du langage. En outre, il y a plusieurs problèmes à prendre en compte dans votre exemple, le code devrait charger les données en mémoire, les stocker, etc.

Enfin, la parallélisation est une fonctionnalité que vous pouvez forcer d'un point de vue de la programmation, mais en résumé, certains processeurs peuvent essayer de penser qu'une partie du code peut être exécutée en même temps, car ils sont indépendants. Dans votre cas, ce n'est clairement pas le cas, car vous devez exécuter les instructions séquentiellement, donc non, cela ne s'exécutera pas en même temps.

SRKX
la source