6
votes

Assemblée du bras: Incrémentation automatique du magasin en magasin

est-il possible d'incrémenter automatiquement l'adresse de base d'un registre sur un STR avec un [rn]! code>? J'ai parcouru la documentation, mais je n'ai pas été en mesure de trouver une réponse définitive, principalement parce que la syntaxe de commandement est présentée à la fois pour la théorie de la LDR et STR-in they, elle devrait fonctionner pour les deux, mais je n'ai pas pu trouver d'exemples d'auto -InCrémenter sur un magasin (le chargement fonctionne bien).

J'ai fait un petit programme qui stocke deux chiffres dans un vecteur. Quand c'est fait le contenu de out code> doit être {1, 2} code> mais le magasin écrase le premier octet, comme si l'incrément automatique ne fonctionne pas. P >

volatile int out[16];


1 commentaires

Diapositive 44, je pense que cela peut mais aucun moyen de tester en ce moment .. SimpleMachines.it/doc/arm_inst. PDF Utilisation du! opérateur


3 Réponses :


6
votes

Pour stocker et charger, vous faites ceci:

83a8:   e59f0014    ldr r0, [pc, #20]   ; 83c4 <main+0x44>
83ac:   e59d1000    ldr r1, [sp]
83b0:   e59d2004    ldr r2, [sp, #4]
83b4:   ebffffe5    bl  8350 <_init+0x20>


18 commentaires

utilisant ! Pour les instructions LDM / STM, le montant à incrémenter ou à décrémenter est déterminé par le nombre de registres de la liste de registres (pas assez de place dans l'instruction pour une constante). La LDR / Str a de la place pour une constante.


Pour cette affaire, vous pouvez utiliser STM, STMIA R0!, {R1} stockera R1 à l'adresse R0, puis "Incrément après" (IA) le magasin. Incrément STMIA APREMENT APREMENT, DRMDB Décrément avant, décrémentation de STMDA après, incrément de STMIB avant.


Bien que la solution fonctionne, j'ai constaté que la sortie du programme est {0, 0} pour le drapeau du compilateur -O2. Toute pensée quant à la raison pour laquelle cela se passe? J'utilise les valeurs de la fonction Printf et je ne reçois pas d'avertissements à la compilation.


Oh je vois, merci pour l'explication. Je dois donc déclarer toutes les variables utilisées dans un environnement volatil ainsi volatil? Et si je les utilise ensuite dans le code C ++ ordinaire, pensez-vous que le code sera lent (dans un cas d'utilisation moyen) en raison du compilateur qui ne peut-il pas optimiser l'accès à la mémoire?


Ok, je viens de comprendre que je peux ajouter "mémoire" à la liste des clobs et obtenir les mêmes résultats que avec le mot clé volatile. Tout va bien maintenant :)


Le problème est que vous utilisiez l'hypothèse selon laquelle la déclaration de variables signifie qu'ils vivent en mémoire, l'optimiseur essaie d'éviter la mémoire dans la mesure du possible / pratique. Ensuite, vous êtes allongé derrière le compilateur et la mémoire manipulée. Donc, l'une ou l'autre solution, indiquez au compilateur que OUT est en mémoire qui change (volatile) ou indiquez au compilateur chaque variable déclarée a été modifiée (CLOBBER). Ou la liste de clobber vous permettra-t-elle de spécifier quelle mémoire a été modifiée afin que vous puissiez cibler la matrice OUT spécifiquement?


Aussi loin que j'ai vu, un emplacement de mémoire spécifique tel qu'un tableau ne peut pas être ciblé, sauf si je le déclare comme volatile. Mettre "mémoire" dans la liste des clobber indique au compilateur de ne pas utiliser les emplacements de mémoire mise en cache (aucun) des registres.


Eh bien, tout cela a travaillé jusqu'à ce que j'ai essayé de faire la même chose avec les instructions néon VSTR / VSTM. J'y incrémente le registre avec! (Comme il s'agit de la manière dont le manuel de référence indique que cela fonctionne), mais le registre modifié est conservé par le compilateur lorsque le drapeau -O2 est défini. J'ai essayé avec "mémoire" et le mot clé volatil mais le comportement est le même. Cela pourrait-il être un punais de compilateur ou y a-t-il une autre chose que j'ai manquée?


Je ne connais pas très bien les instructions du néon. Je n'utilise pas non plus d'assembleur en ligne, j'utilise un assembleur brut / droit et je le liez. Vous êtes probablement un pas en avance sur moi sur le néon.


OK, pour les deux derniers jours, GCC m'a conduit fou, l'optimiseur jumère tout, rendant le binaire inutilisable et déboguer extrêmement difficile. Pouvez-vous s'il vous plaît dites-moi une manière "plus facile" de mettre du code optimisé dans mon programme? Connaissez-vous de tout didacticiel pour cela? J'ai entendu dire que les fichiers d'assemblage bruts sont plus difficiles qu'en ligne, vous devez économiser / charger la pile ou quelque chose comme ça. De plus, lorsque vous proférez votre code, que utilisez-vous pour voir combien de cycles prend-il? J'utilise actuellement une minuterie simple, mais cela donne des résultats assez aléatoires. Merci encore pour vos explications!


Qu'est-ce que vous essayez vraiment de faire, qu'est-ce que c'est que le compilateur n'est pas en train de faire ce mandat en premier lieu? L'assemblage en ligne est extrêmement compilateur spécifique et, comme vous le découvrez, apprenez simplement à vaincre un compilateur dans la soumission pour une tâche est un défi. Je préfère plus de code portable qui repose aussi peu sur le compilateur spécifique que possible. Déterminez qu'il est nécessaire de faire l'optimisation des mains avant de le faire. On dirait que vous fonctionnez sur beaucoup d'hypothèses qui pourraient ne pas être valables.


Pour utiliser droit ASM, vous devez connaître la convention appelante au moins un peu, mais ce n'est pas difficile à comprendre car vous pouvez écrire C code et examiner ce que le compilateur produit pour voir comment fonctionne la Convention. Le bras utilise un registre qui passe pour les premiers mots de mots d'arguments et d'un registre pour le retourner, selon ce que vous faites, vous n'avez peut-être pas besoin de la pile du tout. Oui, car il s'agit essentiellement d'une autre fonction que vous pourriez perdre des horloges dans la préparation pour faire appel à l'appel, il existe un risque similaire avec l'inlinage.


GITUB.COM/DWELCH67/STM32F4D J'ai dobonné un peu avec des instructions au néon / flotteur. N'oubliez pas que c'était sur un cortex-m4 (ne supporte pas les instructions du bras, le pouce / le pouce 2 uniquement), et j'essayais simplement une fracture ou quelque chose du genre qui est plus douloureux avec un point fixe. Connaissant la tâche plus importante en essayant d'être optimisé, vous pouvez également avoir besoin d'un code à la main une partie plus grande, ou de prendre le code généré du compilateur et de la modifier pour l'améliorer.


Le profilage est comme une analyse comparative, il est plus facile de la tromper ou de mal interpréter les résultats que de la faire correctement et d'isoler les vrais problèmes. La réorganisation de quelques lignes de fonctions C ou de scission ou de jonction peut produire plusieurs fois des gains de performance (ou des pertes). Une fois que les caches sont impliquées, vous avez besoin d'une grande expérience de la performance pour comprendre les résultats et quelle est la prochaine étape. Si la performance est importante pour la tâche, bien que vous ayez à apprendre un jour / quelque part.


J'ai commencé avec le zen de la langue de montage par Michael Abrash de retour lorsque vous pourriez réellement acheter ce livre sur l'étagère au magasin neuf. Et ont pratiqué des optimisations de performance depuis (20 ans ou plus). Je viens de tirer ce livre il y a quelques semaines et je l'ai regardé et ce que j'ai appris alors de ce livre, j'utilise toujours tous les jours, pas les détails 8088/86, mais les processus de pensée. Peu importe la qualité que vous pensez que vous êtes à l'optimisation des ressources, vous devez faire du temps votre code, dans le même temps, vous devez savoir comment faire le temps du code et interpréter les résultats.


Wow, merci beaucoup pour les réponses, je vais essayer de garder celles à l'esprit. Je travaille actuellement sur un Pandaboard OMAP 4430 sur lequel je suis en cours d'exécution des algorithmes de traitement d'image. En raison des grandes tailles d'image impliquées, c'est assez lent, alors j'essaie de les optimiser. La mauvaise chose est qu'après passer une semaine sur le code de réécriture pour le néon, la demande fonctionne exactement la même (lente) qu'auparavant, donc je suppose que je devais changer l'algorithme lui-même pour être plus approprié pour le pipeline au néon ou Essayez de trouver d'autres ressources auprès du tableau avec laquelle scinder la charge de travail.


De mon zen de racines de montage et un peu de bon sens. Êtes-vous sûr de cibler le problème de performance approprié? Le problème peut être le déménagement de données autour et pourrait ne pas être la rapidité avec laquelle vous pouvez le calculer. Un équipage de voiture de course pourrait être capable de changer les pneus et l'essence en quelques secondes, mais de la voiture ne fait que 20 miles par heure maximale, vous ne remarquerez pas à quel point l'arrêt des fosses était rapide. Est-il possible de transmettre les données par non-transformé et de voir s'il s'agit de la copie / des E / S et non du traitement?


Si rien d'autre, en mesurant la copie / E / S sans traitement, vous obtenez une idée de la vitesse théorique maximale si le traitement était infiniment rapide. Si vous pouvez déplacer 10 Mbps sans traitement, vous ne pouvez pas vous attendre à pouvoir traiter et déplacer plus rapidement que 10 Mbps sans réparer les chemins d'E / S / Copy. Oui, le cache dégage avec les chiffres et chaque fois que vous touchez le code, il change où les choses se trouvent dans le cache qui jouent à nouveau avec les chiffres ...



0
votes

Assembleur en ligne GCC nécessite que tous les registres modifiés et les variables non volatiles soient répertoriés en tant que sorties ou encombbées. Dans le deuxième exemple, GCC peut et suppose que les registres alloués à dans code> et out code> ne changent pas.

Une approche correcte serait: P>

out_temp = out;
asm volatile ("..." : "+r"(in), "+r"(out_temp) :: "memory" );


0 commentaires

0
votes

J'ai trouvé cette question lors de la recherche de la réponse à une question similaire: comment lier un registre d'entrée / sortie. La documentation du GCC des constructeurs d'assembleurs en ligne indique que le préfixe dans la liste de registre d'entrée désigne un registre d'entrée / sortie.

Dans l'exemple, il me semble que vous préférez préserver le Valeur d'origine de la variable out . Néanmoins, si vous souhaitez utiliser la variante post-incraction (! ) des instructions, je pense que vous devez déclarer les paramètres comme lecture / écriture. Ce qui suit a fonctionné sur ma framboise pi 2: xxx

de cette manière, la sémantique du code est claire au compilateur: le dans et OUT OUT OUT Les pointeurs seront modifiés (incrémentés par 8 éléments).

Disclaimer: Je ne sais pas si le bras ABI permet une fonction de clocer librement les registres du néon D0 à D7. Dans cet exemple simple, cela n'a probablement pas d'importance.


0 commentaires