2016-06-10 50 views
1

我目前正在对ARM处理器上基于Linux的软件本身进行优化。这些优化主要是ARM和ARM NEON功能的形式。但是,一旦我介绍了汇编函数,它们不会堆栈在调用它们的函数之上,而是看起来随机的地方。然而,为了剖析软件,我使用了perf record和flame-graphs。ARM头部以获取适当的调用堆栈

因此,我的问题是,我应该在我的函数中包含哪些内容,以便它们在调用堆栈中正确显示。

有一个稍微相关的主题,但没有给出好的答案How to get call graph profiling working with gcc compiled code and ARM Cortex A8 target?。我使用相同的标志加mapcs-frame。

下面我给出一个由GCC翻译成ARM的C函数的例子。这ARM功能似乎产生体面的堆栈,但我想明白为什么。

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 

-------------------编辑------------------ -

下面是我尝试整合的那种函数的例子。在连接方面,它所做的一切就是在开始时保存堆栈和链接寄存器并将其设置为结束。我应该添加什么?

.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

那么你有什么汇编函数的问题看起来像?你是否正在创建相关的APCS布局堆栈框架? – Notlikethat

+0

值得注意的是,多年前APCS已被EABI所取代,'-mapcs-frame'已经被剥夺了相当长的一段时间,而GCC家伙正在急于完全将其删除。也就是说,FWIW [Rick Murray的网站](http://www.heyrick.co.uk/assembler/apcsintro.html)可能是旧事物最好的参考。 – Notlikethat

+0

我添加了一种编辑功能,我尝试进行配置文件,我不认为我正在创建堆栈帧。 – VictorC

回答

0

你可以清楚地看到,编译器是如何标注一些伪操作汇编...

.global mean 
    .type mean, %function 

...

.size mean, .-mean 

这些投入COFF部分,并且需要对其进行编译,以便调用图工具可以知道您的汇编程序函数的范围是PC

.global ARM_smoothing 
+ .type ARM_smoothing, %function 

...

+ .size ARM_smoothing, .-ARM_smoothing 

其他伪操作取决于所需的调试信息。

其他都.fnend.fnstart.movsp.save.setfp等。

它取决于工具所期望的调试/对象格式。还有两种类型的数据;

  1. 代码程度信息
  2. 堆栈和帧使用

两者都通常需要用于展开(或叠层回溯),但采样性能工具可能仅逃脱第一。执行对象清理的异常处理代码需要最多的信息。

相关:ARM Link and frame register

+0

这不是勺子饲料的直接答案;您需要查阅GAS文档页面并查看编译器的汇编程序输出。检查你的工具文档,对象(COFF/ELF)也会有所帮助。希望这个*答案在指导许多人解决问题而不是针对某个特定实例时很有用。 –