2016-06-10 5 views
1

Je réalise actuellement des optimisations sur un logiciel basé sur linux lui-même sur un processeur ARM. Ces optimisations se présentent principalement sous la forme de fonctions ARM et ARM NEON. Afin de profiler le logiciel, j'utilise des enregistrements de perf et des graphiques de flammes, cependant, une fois que j'introduis les fonctions d'assembleur, ils ne s'empilent pas sur les fonctions qui les appellent mais plutôt sur des endroits apparemment aléatoires.En-têtes ARM pour obtenir des piles d'appels appropriées

Ma question était donc, que devrais-je inclure dans mes fonctions pour qu'ils apparaissent correctement dans les piles d'appels.

Il y avait un sujet légèrement lié mais aucune bonne réponse n'a été donnée How to get call graph profiling working with gcc compiled code and ARM Cortex A8 target?. J'utilise les mêmes drapeaux plus mapcs-frame. Ci-dessous, je donne un exemple de fonction C traduite en ARM par GCC. Cette fonction ARM semble produire des piles décentes mais je voudrais comprendre pourquoi.

int half(int in); 
int sum(int in1, int in2); 
int mean(int in1, int in2); 

int half(int i) 
{ 
    return i/2; 
} 

int sum(int i, int j) 
{ 
    return i + j; 
} 

int mean(int i, int j) 
{ 
    int s = sum(i, j); 
    int m = half(s); 
    return m; 
} 

int main() 
{ 
    int a = 1; 
    int b = 5; 
    int i; 
    int result; 
    for (i = 0; i<10000000; i++) { 
     result = mean(a, b); 
    } 
    return 0; 
} 
.cpu cortex-a9 
     .eabi_attribute 27, 3 
     .eabi_attribute 28, 1 
     .fpu neon 
     .eabi_attribute 20, 1 
     .eabi_attribute 21, 1 
     .eabi_attribute 23, 3 
     .eabi_attribute 24, 1 
     .eabi_attribute 25, 1 
     .eabi_attribute 26, 2 
     .eabi_attribute 30, 6 
     .eabi_attribute 34, 1 
     .eabi_attribute 18, 4 
     .file "a.c" 
     .text 
     .align 2 
     .global half 
     .type half, %function 
    half: 
     @ args = 0, pretend = 0, frame = 8 
     @ frame_needed = 1, uses_anonymous_args = 0 
     mov ip, sp 
     stmfd sp!, {fp, ip, lr, pc} 
     sub fp, ip, #4 
     sub sp, sp, #8 
     str r0, [fp, #-16] 
     ldr r3, [fp, #-16] 
     mov r2, r3, lsr #31 
     add r3, r2, r3 
     mov r3, r3, asr #1 
     mov r0, r3 
     sub sp, fp, #12 
     ldmfd sp, {fp, sp, pc} 
     .size half, .-half 
     .align 2 
     .global sum 
     .type sum, %function 
    sum: 
     @ args = 0, pretend = 0, frame = 8 
     @ frame_needed = 1, uses_anonymous_args = 0 
     mov ip, sp 
     stmfd sp!, {fp, ip, lr, pc} 
     sub fp, ip, #4 
     sub sp, sp, #8 
     str r0, [fp, #-16] 
     str r1, [fp, #-20] 
     ldr r2, [fp, #-16] 
     ldr r3, [fp, #-20] 
     add r3, r2, r3 
     mov r0, r3 
     sub sp, fp, #12 
     ldmfd sp, {fp, sp, pc} 
     .size sum, .-sum 
     .align 2 
     .global mean 
     .type mean, %function 
    mean: 
     @ args = 0, pretend = 0, frame = 16 
     @ frame_needed = 1, uses_anonymous_args = 0 
     mov ip, sp 
     stmfd sp!, {fp, ip, lr, pc} 
     sub fp, ip, #4 
     sub sp, sp, #16 
     str r0, [fp, #-24] 
     str r1, [fp, #-28] 
     ldr r1, [fp, #-28] 
     ldr r0, [fp, #-24] 
     bl sum 
     str r0, [fp, #-16] 
     ldr r0, [fp, #-16] 
     bl half 
     str r0, [fp, #-20] 
     ldr r3, [fp, #-20] 
     mov r0, r3 
     sub sp, fp, #12 
     ldmfd sp, {fp, sp, pc} 
     .size mean, .-mean 
     .align 2 
     .global main 
     .type main, %function 
    main: 
     @ args = 0, pretend = 0, frame = 16 
     @ frame_needed = 1, uses_anonymous_args = 0 
     mov ip, sp 
     stmfd sp!, {fp, ip, lr, pc} 
     sub fp, ip, #4 
     sub sp, sp, #16 
     mov r3, #1 
     str r3, [fp, #-20] 
     mov r3, #5 
     str r3, [fp, #-24] 
     mov r3, #0 
     str r3, [fp, #-16] 
     b .L8 
    .L9: 
     ldr r1, [fp, #-24] 
     ldr r0, [fp, #-20] 
     bl mean 
     str r0, [fp, #-28] 
     ldr r3, [fp, #-16] 
     add r3, r3, #1 
     str r3, [fp, #-16] 
    .L8: 
     ldr r2, [fp, #-16] 
     movw r3, #38527 
     movt r3, 152 
     cmp r2, r3 
     ble .L9 
     mov r3, #0 
     mov r0, r3 
     sub sp, fp, #12 
     ldmfd sp, {fp, sp, pc} 
     .size main, .-main 
     .ident "GCC: (crosstool-NG linaro-1.13.1-4.9-2014.09 - Linaro GCC 4.9-2014.09) 4.9.2 20140904 (prerelease)" 
     .section .note.GNU-stack,"",%progbits 

------------------- EDIT ------------------ -

Voici l'exemple du type de fonction que j'essaie d'intégrer. En termes de linkage, tout ce qu'il fait est de sauvegarder la pile et le registre de liaison au début et de leur donner la fin. Que devrais-je ajouter?

.section .text 

.global ARM_smoothing 

ARM_smoothing: 
    STMFD  sp!, {r4-r12,lr} //move used registers on stack (avoid segmentation fault) 
    MOV   r5, r0    
    ADD   r0, r0, r2 
    ADD   r0, r0, r2 
    MOV   r8, r0 
    ADD   r8, r8, r2 
    ADD   r8, r8, r2  //the 6 instructions create 3 pointers to the row above and below as well as the current one 
    ADD   r1, r1, r2 
    ADD   r1, r1, r2  
    ADD   r1, r1, #2  //move destination pointer to first element (1 row down, 1 element left) 
    SUB   r2, r2, #2 
    SUB   r3, r3, #2  //counters decremented because smoothing function works with a margin of 1 on every side 
    LDR   r9, =0x1C71C71D //(1/9)*2^32 pour effectuer la division par 9 
    LDR   r10, =0x2 
    LDR   r11, =0xC  //shifts for pointers to data 
    VLDR.U64 d20, =0x1C71C71D //(1/9)*2^32 pour effectuer la division par 9 
    VLDR.U64 d22, =0x0  //initialization of zeros to be used (not ncessarily needed) 
    VLDR.U64 d23, =0x0 
    VDUP.32  d20, d20[0]  //initialize vector for multiplication 
height_loop: 
    MOV   r4, r2   //reset width counter 
    CMP   r4, #8 
    BLGE  width_loop_eight_smoothing //use neon while more than 8 elements in row need smoothing 
    CMP   r4, #1 
    BLGE  width_loop_rest //use normal ARM for remaining elements, can't do in NEON because of margin 
    ADD   r0, r0, #4  //skip margin 
    ADD   r1, r1, #4 
    ADD   r5, r5, #4 
    ADD   r8, r8, #4 
    SUBS  r3, r3, #1  //decrement row counter 
    BNE   height_loop  //loop while there still are rows 
    LDMFD  sp!, {r4-r12,pc} //restore stack and return to calling function 


width_loop_eight_smoothing: 
    SUB   r4, r4, #8  //decrement width counter 
    VLD1.16  {d0, d1}, [r5], r10  //load upper left elements 
    VLD1.16  {d2, d3}, [r5], r10  //load upper middle elements 
    VADDL.S16 q2, d0, d2    //long addition of elements to be sure to not lose any data 
    VADDL.S16 q3, d1, d3    
    VLD1.16  {d0, d1}, [r5], r11  //load upper right elements  
    VLD1.16  {d2, d3}, [r0], r10  //load middle left elements 
    VADDL.S16 q4, d0, d2 
    VADDL.S16 q5, d1, d3 
    VADD.S32 q2, q4     //add to grand total 
    VADD.S32 q3, q5 
    VLD1.16  {d0, d1}, [r0], r10  //load current elements 
    VLD1.16  {d2, d3}, [r0], r11  //load middle right elements 
    VADDL.S16 q4, d0, d2 
    VADDL.S16 q5, d1, d3 
    VADD.S32 q2, q4 
    VADD.S32 q3, q5 
    VLD1.16  {d0, d1}, [r8], r10  //load lower left elements 
    VLD1.16  {d2, d3}, [r8], r10  //load lower middle elements 
    VADDL.S16 q4, d0, d2 
    VADDL.S16 q5, d1, d3 
    VADD.S32 q2, q4 
    VADD.S32 q3, q5 
    VLD1.16  {d0, d1}, [r8], r11  //load lower right elements 
    VADDL.S16 q4, d0, d22 
    VADDL.S16 q5, d1, d23 
    VADD.S32 q2, q4 
    VADD.S32 q3, q5 
    VMULL.S32 q6, d4, d20    //divide by 9 (upper element is total divided by 9) 
    VMULL.S32 q7, d5, d20 
    VMULL.S32 q8, d6, d20 
    VMULL.S32 q9, d7, d20 
    VUZP.32  q6, q7     //pack results into less registers and smaller elements 
    VUZP.32  q8, q9 
    VUZP.16  q7, q9 
    VSHR.U16 q8, q7, #15    //when multiplied element is negative, result is always one under 
    VADD.S16 q7, q8     //rectifying by adding sign bit to total 
    VST1.16  {d14, d15}, [r1]!  //store results 
    CMP   r4, #8     //check if theres enough elements to do 8 more in NEON   
    BCS   width_loop_eight_smoothing  //if yes, loop neon code 
    MOV   PC, LR     //return to ARM_smoothing if not 



width_loop_rest:      //works similaarly to NEON but one element at a time 
    LDRSH  r6, [r0], #2   //converts loaded half words to signed full words 
    LDRSH  r7, [r0]    //main difference is with the way increments are done since there is an overlap 
    ADD   r6, r7, r6 
    LDRSH  r7, [r0, #2] 
    ADD   r6, r7, r6 
    LDRSH  r7, [r5], #2 
    ADD   r6, r7, r6 
    LDRSH  r7, [r5] 
    ADD   r6, r7, r6 
    LDRSH  r7, [r5, #2] 
    ADD   r6, r7, r6 
    LDRSH  r7, [r8], #2 
    ADD   r6, r7, r6 
    LDRSH  r7, [r8] 
    ADD   r6, r7, r6 
    LDRSH  r7, [r8, #2] 
    ADD   r6, r7, r6 
    SMULLS  r6, r7, r6, r9 
    ADDMI  r7, #1 
    STRH  r7, [r1], #2 
    SUBS  r4, #1   //decrement width counter and check if there's any left 
    BNE   width_loop_rest 
    MOV   PC, LR 
+0

Alors, que pensez-vous de la fonction de montage en question ressemble? Créez-vous le cadre de pile de mise en page APCS approprié? – Notlikethat

+0

Il peut également être intéressant de noter qu'APCS a été remplacé par EABI il y a des années, '-mapcs-frame' a été déprécié depuis un certain temps et les gars du GCC sont désespérés de le supprimer entièrement. Cela dit, FWIW [site de Rick Murray] (http://www.heyrick.co.uk/assembler/apcsintro.html) est probablement la meilleure référence pour les vieux trucs. – Notlikethat

+0

J'ai ajouté un edit avec le genre de fonctions que j'essaye de profiler, je ne pense pas que je crée les frames de la pile. – VictorC

Répondre

0

Vous pouvez voir clairement comment le compilateur est annoter l'assembleur avec quelques pseudo-ops ...

.global mean 
    .type mean, %function 

...

.size mean, .-mean 

Ceux-ci sont mis en COFF sections et doivent le faire à une construction de sorte que les outils de graphique d'appel peuvent savoir ce que la gamme PC est pour votre fonction d'assembleur.

.global ARM_smoothing 
+ .type ARM_smoothing, %function 

...

+ .size ARM_smoothing, .-ARM_smoothing 

D'autres pseudo-ops dépendent des informations de débogage nécessaires.

D'autres sont .fnend, .fnstart, .movsp, .save, .setfp, etc.

Cela dépend du format de débogage/objet attendu par l'outil.Il y a aussi deux types de données;

  1. mesure les informations de code
  2. pile et cadre l'utilisation

sont généralement nécessaires deux pour se détendre (ou une pile de retraçage), mais un outil de performance d'échantillonnage pourrait ne sortir avec le premier. Le code de gestion des exceptions qui nettoie l'objet nécessite le plus d'informations.

connexes: ARM Link and frame register

+0

Ce n'est pas une réponse directe de cuillère nourrir; vous devez consulter les pages de documentation du GAS et regarder la sortie de l'assembleur du compilateur. En examinant la documentation de votre outil, l'objet (COFF/ELF) vous sera également utile. Espérons que cette * réponse * est utile pour guider de nombreuses personnes vers une solution plutôt qu'une pour une instance particulière. –